Add BasicApi and BasicViews apps
- #7805 - make initial copy of apps from aspnet/Performance repo - add apps to solution - add Readme for the benchmark apps - update BasicApi app to actually do authentication and authorization - bug in the ported app - refactor `Main` methods and add `CreateWebHostBuilder(...)` methods - change projects to understand `$(BenchmarksTargetFramework)` - use NuGet.org EF packages to avoid changing the Universe build graph - use SQLite instead of LocalDb by default - remove unnecessary appsettings.json files and JSON configuration support - add EF migrations - (greatly) reduce startup times compared to creating / deleting databases - add MySql, PostgreSQL, and SqlServer support - load BasicApi data in a `DbContext.OnModelCreating(...)` override - no longer need seed.sql - generalize migrations to support multiple providers - use negative seeding indices to work around npgsql/Npgsql.EntityFrameworkCore.PostgreSQL#36 - work around Pomelo lack of strong name (PomeloFoundation/Pomelo.EntityFrameworkCore.MySql#603) - use BenchmarksOnly* properties for EF dependencies - see also aspnet/Universe#1224 - drop databases (if SQLite) or migrations (otherwise) in `IApplicationLifetime.ApplicationStopping` handlers - add functional tests - drop SQLite database at end of test run - add benchmarks automation - add anonymous BasicApi action i.e. require no authorization - add non-antiforgery BasicViews actions Address PR comments - remove `AntiforgeryTestHelper` workarounds - use `[ApiController]` - use `ActionResult<Pet>` - remove unused classes nits: - take VS suggestions in added files - optionally display create and delete SQL scripts for per-database migrations - merge `InsertData(...)` calls for consistency with most supported EF providers - SQLite is the only one that requires separate `INSERT`s and EF does the splitting
This commit is contained in:
parent
6911e192e4
commit
53857d052f
32
Mvc.sln
32
Mvc.sln
|
|
@ -170,6 +170,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorPagesClassLibrary", "t
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Views.TestCommon", "test\Microsoft.AspNetCore.Mvc.Views.TestCommon\Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj", "{51E3E785-A9D1-4196-BAFE-A17FF4304B89}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarkapps", "benchmarkapps", "{2859F266-673A-45A2-9E3C-7B39C6DDD38E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicApi", "benchmarkapps\BasicApi\BasicApi.csproj", "{910F023A-88E3-4CB4-8793-AC4005C7B421}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicViews", "benchmarkapps\BasicViews\BasicViews.csproj", "{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -894,6 +900,30 @@ Global
|
|||
{51E3E785-A9D1-4196-BAFE-A17FF4304B89}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{51E3E785-A9D1-4196-BAFE-A17FF4304B89}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{51E3E785-A9D1-4196-BAFE-A17FF4304B89}.Release|x86.Build.0 = Release|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -963,6 +993,8 @@ Global
|
|||
{E83D3745-9BCF-40E8-8D34-AFBA604C2439} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||
{17122147-ADFD-41C8-87D9-CCC582CCA8F9} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
{51E3E785-A9D1-4196-BAFE-A17FF4304B89} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||
{910F023A-88E3-4CB4-8793-AC4005C7B421} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
|
||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {63D344F6-F86D-40E6-85B9-0AABBE338C4A}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(BenchmarksTargetFramework)' != ''">$(BenchmarksTargetFramework)</TargetFrameworks>
|
||||
|
||||
<DefineConstants Condition=" '$(GenerateSqlScripts)'=='true' ">$(DefineConstants);GENERATE_SQL_SCRIPTS</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_SQL_SCRIPTS</DefineConstants>
|
||||
|
||||
<WarningsNotAsErrors>CS8002;$(WarningsNotAsErrors)</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(BenchmarksOnlyNpgsqlEntityFrameworkCorePostgreSQLPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net461'">
|
||||
<PackageReference Include="MySqlConnector" Version="$(BenchmarksOnlyMySqlConnectorPackageVersion)" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="$(BenchmarksOnlyPomeloEntityFrameworkCoreMySqlPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- These references are used when running locally -->
|
||||
<ItemGroup Condition="'$(BenchmarksTargetFramework)' == ''">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="$(BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion)" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlitePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlServerPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.DataAnnotations\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Formatters.Json\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
These references are used when running on the Benchmarks Server.
|
||||
Use All meta-package and not App to include Microsoft.EntityFrameworkCore.Sqlite.
|
||||
-->
|
||||
<ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(MicrosoftAspNetCoreAllPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BasicApi.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BasicApi.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Authorize("pet-store-reader")]
|
||||
[Route("/pet")]
|
||||
public class PetController : ControllerBase
|
||||
{
|
||||
public PetController(BasicApiContext dbContext)
|
||||
{
|
||||
DbContext = dbContext;
|
||||
}
|
||||
|
||||
public BasicApiContext DbContext { get; }
|
||||
|
||||
[HttpGet("{id}", Name = "FindPetById")]
|
||||
public async Task<ActionResult<Pet>> FindById(int id)
|
||||
{
|
||||
var pet = await DbContext.Pets
|
||||
.Include(p => p.Category)
|
||||
.Include(p => p.Images)
|
||||
.Include(p => p.Tags)
|
||||
.FirstOrDefaultAsync(p => p.Id == id);
|
||||
if (pet == null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
return pet;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("anonymous/{id}")]
|
||||
public async Task<ActionResult<Pet>> FindByIdWithoutToken(int id)
|
||||
{
|
||||
var pet = await DbContext.Pets
|
||||
.Include(p => p.Category)
|
||||
.Include(p => p.Images)
|
||||
.Include(p => p.Tags)
|
||||
.FirstOrDefaultAsync(p => p.Id == id);
|
||||
if (pet == null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
return pet;
|
||||
}
|
||||
|
||||
[HttpGet("findByCategory/{categoryId}")]
|
||||
public async Task<ActionResult<Pet>> FindByCategory(int categoryId)
|
||||
{
|
||||
var pet = await DbContext.Pets
|
||||
.Include(p => p.Category)
|
||||
.Include(p => p.Images)
|
||||
.Include(p => p.Tags)
|
||||
.FirstOrDefaultAsync(p => p.Category != null && p.Category.Id == categoryId);
|
||||
if (pet == null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
return pet;
|
||||
}
|
||||
|
||||
[HttpGet("findByStatus")]
|
||||
public async Task<ActionResult<Pet>> FindByStatus(string status)
|
||||
{
|
||||
var pet = await DbContext.Pets
|
||||
.Include(p => p.Category)
|
||||
.Include(p => p.Images)
|
||||
.Include(p => p.Tags)
|
||||
.FirstOrDefaultAsync(p => p.Status == status);
|
||||
if (pet == null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
return pet;
|
||||
}
|
||||
|
||||
[HttpGet("findByTags")]
|
||||
public async Task<ActionResult<Pet>> FindByTags(string[] tags)
|
||||
{
|
||||
var pet = await DbContext.Pets
|
||||
.Include(p => p.Category)
|
||||
.Include(p => p.Images)
|
||||
.Include(p => p.Tags)
|
||||
.FirstOrDefaultAsync(p => p.Tags.Any(t => tags.Contains(t.Name)));
|
||||
if (pet == null)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
return pet;
|
||||
}
|
||||
|
||||
[Authorize("pet-store-writer")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddPet([FromBody] Pet pet)
|
||||
{
|
||||
DbContext.Pets.Add(pet);
|
||||
await DbContext.SaveChangesAsync();
|
||||
|
||||
return new CreatedAtRouteResult("FindPetById", new { id = pet.Id }, pet);
|
||||
}
|
||||
|
||||
[Authorize("pet-store-writer")]
|
||||
[HttpPut]
|
||||
public IActionResult EditPet(Pet pet)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Authorize("pet-store-writer")]
|
||||
[HttpPost("{id}/uploadImage")]
|
||||
public IActionResult UploadImage(int id, IFormFile file)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Authorize("pet-store-writer")]
|
||||
[HttpDelete("{id}")]
|
||||
public IActionResult DeletePet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace BasicApi.Controllers
|
||||
{
|
||||
public class TokenController : ControllerBase
|
||||
{
|
||||
private static readonly Dictionary<string, ClaimsIdentity> _identities;
|
||||
|
||||
static TokenController()
|
||||
{
|
||||
_identities = new Dictionary<string, ClaimsIdentity>(StringComparer.Ordinal);
|
||||
|
||||
var reader = new ClaimsIdentity();
|
||||
reader.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, "reader@example.com"));
|
||||
reader.AddClaim(new Claim("scope", "pet-store-reader"));
|
||||
_identities.Add("reader@example.com", reader);
|
||||
|
||||
var writer = new ClaimsIdentity();
|
||||
writer.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, "writer@example.com"));
|
||||
writer.AddClaim(new Claim("scope", "pet-store-reader"));
|
||||
writer.AddClaim(new Claim("scope", "pet-store-writer"));
|
||||
_identities.Add("writer@example.com", writer);
|
||||
}
|
||||
|
||||
private readonly SigningCredentials _credentials;
|
||||
private readonly JwtBearerOptions _options;
|
||||
|
||||
public TokenController(
|
||||
IOptionsSnapshot<JwtBearerOptions> options,
|
||||
SigningCredentials credentials)
|
||||
{
|
||||
_options = options.Get(JwtBearerDefaults.AuthenticationScheme);
|
||||
_credentials = credentials;
|
||||
}
|
||||
|
||||
[HttpGet("/token")]
|
||||
public IActionResult GetToken(string username)
|
||||
{
|
||||
if (username == null || !_identities.TryGetValue(username, out var identity))
|
||||
{
|
||||
return new StatusCodeResult(403);
|
||||
}
|
||||
|
||||
var handler = _options.SecurityTokenValidators.OfType<JwtSecurityTokenHandler>().First();
|
||||
var tokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Issuer = _options.TokenValidationParameters.ValidIssuer,
|
||||
Audience = _options.TokenValidationParameters.ValidAudience,
|
||||
SigningCredentials = _credentials,
|
||||
Subject = identity
|
||||
};
|
||||
|
||||
var securityToken = handler.CreateJwtSecurityToken(
|
||||
issuer: _options.TokenValidationParameters.ValidIssuer,
|
||||
audience: _options.TokenValidationParameters.ValidAudience,
|
||||
signingCredentials: _credentials,
|
||||
subject: identity);
|
||||
|
||||
var token = handler.WriteToken(securityToken);
|
||||
return Content(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
172
benchmarkapps/BasicApi/Migrations/20180609000420_InitialCreate.Designer.cs
generated
Normal file
172
benchmarkapps/BasicApi/Migrations/20180609000420_InitialCreate.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// <auto-generated />
|
||||
using BasicApi.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace BasicApi.Migrations
|
||||
{
|
||||
[DbContext(typeof(BasicApiContext))]
|
||||
[Migration("20180609000420_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "2.1.0-rtm-30799")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Category", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, Name = "Dogs" },
|
||||
new { Id = -2, Name = "Cats" },
|
||||
new { Id = -3, Name = "Rabbits" },
|
||||
new { Id = -4, Name = "Lions" }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Image", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int?>("PetId");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PetId");
|
||||
|
||||
b.ToTable("Images");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, PetId = -1, Url = "http://example.com/pets/-1_1.png" },
|
||||
new { Id = -2, PetId = -2, Url = "http://example.com/pets/-2_1.png" },
|
||||
new { Id = -3, PetId = -3, Url = "http://example.com/pets/-3_1.png" },
|
||||
new { Id = -4, PetId = -4, Url = "http://example.com/pets/-4_1.png" },
|
||||
new { Id = -5, PetId = -5, Url = "http://example.com/pets/-5_1.png" },
|
||||
new { Id = -6, PetId = -6, Url = "http://example.com/pets/-6_1.png" },
|
||||
new { Id = -7, PetId = -7, Url = "http://example.com/pets/-7_1.png" },
|
||||
new { Id = -8, PetId = -8, Url = "http://example.com/pets/-8_1.png" },
|
||||
new { Id = -9, PetId = -9, Url = "http://example.com/pets/-9_1.png" },
|
||||
new { Id = -10, PetId = -10, Url = "http://example.com/pets/-10_1.png" },
|
||||
new { Id = -11, PetId = -11, Url = "http://example.com/pets/-11_1.png" },
|
||||
new { Id = -12, PetId = -12, Url = "http://example.com/pets/-12_1.png" }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Pet", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("Age");
|
||||
|
||||
b.Property<int?>("CategoryId");
|
||||
|
||||
b.Property<bool>("HasVaccinations");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Pets");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, Age = 1, CategoryId = -1, HasVaccinations = true, Name = "Dogs1", Status = "available" },
|
||||
new { Id = -2, Age = 1, CategoryId = -1, HasVaccinations = true, Name = "Dogs2", Status = "available" },
|
||||
new { Id = -3, Age = 1, CategoryId = -1, HasVaccinations = true, Name = "Dogs3", Status = "available" },
|
||||
new { Id = -4, Age = 1, CategoryId = -2, HasVaccinations = true, Name = "Cats1", Status = "available" },
|
||||
new { Id = -5, Age = 1, CategoryId = -2, HasVaccinations = true, Name = "Cats2", Status = "available" },
|
||||
new { Id = -6, Age = 1, CategoryId = -2, HasVaccinations = true, Name = "Cats3", Status = "available" },
|
||||
new { Id = -7, Age = 1, CategoryId = -3, HasVaccinations = true, Name = "Rabbits1", Status = "available" },
|
||||
new { Id = -8, Age = 1, CategoryId = -3, HasVaccinations = true, Name = "Rabbits2", Status = "available" },
|
||||
new { Id = -9, Age = 1, CategoryId = -3, HasVaccinations = true, Name = "Rabbits3", Status = "available" },
|
||||
new { Id = -10, Age = 1, CategoryId = -4, HasVaccinations = true, Name = "Lions1", Status = "available" },
|
||||
new { Id = -11, Age = 1, CategoryId = -4, HasVaccinations = true, Name = "Lions2", Status = "available" },
|
||||
new { Id = -12, Age = 1, CategoryId = -4, HasVaccinations = true, Name = "Lions3", Status = "available" }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Tag", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("PetId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PetId");
|
||||
|
||||
b.ToTable("Tags");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, Name = "Tag1", PetId = -1 },
|
||||
new { Id = -2, Name = "Tag1", PetId = -2 },
|
||||
new { Id = -3, Name = "Tag1", PetId = -3 },
|
||||
new { Id = -4, Name = "Tag1", PetId = -4 },
|
||||
new { Id = -5, Name = "Tag1", PetId = -5 },
|
||||
new { Id = -6, Name = "Tag1", PetId = -6 },
|
||||
new { Id = -7, Name = "Tag1", PetId = -7 },
|
||||
new { Id = -8, Name = "Tag1", PetId = -8 },
|
||||
new { Id = -9, Name = "Tag1", PetId = -9 },
|
||||
new { Id = -10, Name = "Tag1", PetId = -10 },
|
||||
new { Id = -11, Name = "Tag1", PetId = -11 },
|
||||
new { Id = -12, Name = "Tag1", PetId = -12 }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Image", b =>
|
||||
{
|
||||
b.HasOne("BasicApi.Models.Pet")
|
||||
.WithMany("Images")
|
||||
.HasForeignKey("PetId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Pet", b =>
|
||||
{
|
||||
b.HasOne("BasicApi.Models.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Tag", b =>
|
||||
{
|
||||
b.HasOne("BasicApi.Models.Pet")
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("PetId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace BasicApi.Migrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Categories",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
#if !NET461
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
|
||||
#endif
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Categories", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Pets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
#if !NET461
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
|
||||
#endif
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Age = table.Column<int>(nullable: false),
|
||||
CategoryId = table.Column<int>(nullable: true),
|
||||
HasVaccinations = table.Column<bool>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 50, nullable: false),
|
||||
Status = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Pets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Pets_Categories_CategoryId",
|
||||
column: x => x.CategoryId,
|
||||
principalTable: "Categories",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Images",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
#if !NET461
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
|
||||
#endif
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Url = table.Column<string>(nullable: true),
|
||||
PetId = table.Column<int>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Images", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Images_Pets_PetId",
|
||||
column: x => x.PetId,
|
||||
principalTable: "Pets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Tags",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
#if !NET461
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
|
||||
#endif
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
PetId = table.Column<int>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Tags", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Tags_Pets_PetId",
|
||||
column: x => x.PetId,
|
||||
principalTable: "Pets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "Categories",
|
||||
columns: new[] { "Id", "Name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ -1, "Dogs" },
|
||||
{ -2, "Cats" },
|
||||
{ -3, "Rabbits" },
|
||||
{ -4, "Lions" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "Pets",
|
||||
columns: new[] { "Id", "Age", "CategoryId", "HasVaccinations", "Name", "Status" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ -1, 1, -1, true, "Dogs1", "available" },
|
||||
{ -2, 1, -1, true, "Dogs2", "available" },
|
||||
{ -3, 1, -1, true, "Dogs3", "available" },
|
||||
|
||||
{ -4, 1, -2, true, "Cats1", "available" },
|
||||
|
||||
{ -5, 1, -2, true, "Cats2", "available" },
|
||||
|
||||
{ -6, 1, -2, true, "Cats3", "available" },
|
||||
|
||||
{ -7, 1, -3, true, "Rabbits1", "available" },
|
||||
|
||||
{ -8, 1, -3, true, "Rabbits2", "available" },
|
||||
|
||||
{ -9, 1, -3, true, "Rabbits3", "available" },
|
||||
|
||||
{ -10, 1, -4, true, "Lions1", "available" },
|
||||
{ -11, 1, -4, true, "Lions2", "available" },
|
||||
{ -12, 1, -4, true, "Lions3", "available" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "Images",
|
||||
columns: new[] { "Id", "PetId", "Url" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ -1, -1, "http://example.com/pets/-1_1.png" },
|
||||
{ -2, -2, "http://example.com/pets/-2_1.png" },
|
||||
{ -11, -11, "http://example.com/pets/-11_1.png" },
|
||||
{ -3, -3, "http://example.com/pets/-3_1.png" },
|
||||
{ -4, -4, "http://example.com/pets/-4_1.png" },
|
||||
|
||||
{ -10, -10, "http://example.com/pets/-10_1.png" },
|
||||
|
||||
{ -5, -5, "http://example.com/pets/-5_1.png" },
|
||||
|
||||
{ -6, -6, "http://example.com/pets/-6_1.png" },
|
||||
|
||||
{ -12, -12, "http://example.com/pets/-12_1.png" },
|
||||
|
||||
{ -7, -7, "http://example.com/pets/-7_1.png" },
|
||||
{ -9, -9, "http://example.com/pets/-9_1.png" },
|
||||
{ -8, -8, "http://example.com/pets/-8_1.png" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "Tags",
|
||||
columns: new[] { "Id", "Name", "PetId" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ -11, "Tag1", -11 },
|
||||
{ -10, "Tag1", -10 },
|
||||
{ -9, "Tag1", -9 },
|
||||
{ -6, "Tag1", -6 },
|
||||
{ -7, "Tag1", -7 },
|
||||
{ -5, "Tag1", -5 },
|
||||
{ -4, "Tag1", -4 },
|
||||
{ -3, "Tag1", -3 },
|
||||
{ -2, "Tag1", -2 },
|
||||
{ -1, "Tag1", -1 },
|
||||
{ -8, "Tag1", -8 },
|
||||
{ -12, "Tag1", -12 }
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Images_PetId",
|
||||
table: "Images",
|
||||
column: "PetId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Pets_CategoryId",
|
||||
table: "Pets",
|
||||
column: "CategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tags_PetId",
|
||||
table: "Tags",
|
||||
column: "PetId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Images");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Tags");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Pets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Categories");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
// <auto-generated />
|
||||
using BasicApi.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace BasicApi.Migrations
|
||||
{
|
||||
[DbContext(typeof(BasicApiContext))]
|
||||
partial class BasicApiContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "2.1.0-rtm-30799")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Category", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, Name = "Dogs" },
|
||||
new { Id = -2, Name = "Cats" },
|
||||
new { Id = -3, Name = "Rabbits" },
|
||||
new { Id = -4, Name = "Lions" }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Image", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int?>("PetId");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PetId");
|
||||
|
||||
b.ToTable("Images");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, PetId = -1, Url = "http://example.com/pets/-1_1.png" },
|
||||
new { Id = -2, PetId = -2, Url = "http://example.com/pets/-2_1.png" },
|
||||
new { Id = -3, PetId = -3, Url = "http://example.com/pets/-3_1.png" },
|
||||
new { Id = -4, PetId = -4, Url = "http://example.com/pets/-4_1.png" },
|
||||
new { Id = -5, PetId = -5, Url = "http://example.com/pets/-5_1.png" },
|
||||
new { Id = -6, PetId = -6, Url = "http://example.com/pets/-6_1.png" },
|
||||
new { Id = -7, PetId = -7, Url = "http://example.com/pets/-7_1.png" },
|
||||
new { Id = -8, PetId = -8, Url = "http://example.com/pets/-8_1.png" },
|
||||
new { Id = -9, PetId = -9, Url = "http://example.com/pets/-9_1.png" },
|
||||
new { Id = -10, PetId = -10, Url = "http://example.com/pets/-10_1.png" },
|
||||
new { Id = -11, PetId = -11, Url = "http://example.com/pets/-11_1.png" },
|
||||
new { Id = -12, PetId = -12, Url = "http://example.com/pets/-12_1.png" }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Pet", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("Age");
|
||||
|
||||
b.Property<int?>("CategoryId");
|
||||
|
||||
b.Property<bool>("HasVaccinations");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Pets");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, Age = 1, CategoryId = -1, HasVaccinations = true, Name = "Dogs1", Status = "available" },
|
||||
new { Id = -2, Age = 1, CategoryId = -1, HasVaccinations = true, Name = "Dogs2", Status = "available" },
|
||||
new { Id = -3, Age = 1, CategoryId = -1, HasVaccinations = true, Name = "Dogs3", Status = "available" },
|
||||
new { Id = -4, Age = 1, CategoryId = -2, HasVaccinations = true, Name = "Cats1", Status = "available" },
|
||||
new { Id = -5, Age = 1, CategoryId = -2, HasVaccinations = true, Name = "Cats2", Status = "available" },
|
||||
new { Id = -6, Age = 1, CategoryId = -2, HasVaccinations = true, Name = "Cats3", Status = "available" },
|
||||
new { Id = -7, Age = 1, CategoryId = -3, HasVaccinations = true, Name = "Rabbits1", Status = "available" },
|
||||
new { Id = -8, Age = 1, CategoryId = -3, HasVaccinations = true, Name = "Rabbits2", Status = "available" },
|
||||
new { Id = -9, Age = 1, CategoryId = -3, HasVaccinations = true, Name = "Rabbits3", Status = "available" },
|
||||
new { Id = -10, Age = 1, CategoryId = -4, HasVaccinations = true, Name = "Lions1", Status = "available" },
|
||||
new { Id = -11, Age = 1, CategoryId = -4, HasVaccinations = true, Name = "Lions2", Status = "available" },
|
||||
new { Id = -12, Age = 1, CategoryId = -4, HasVaccinations = true, Name = "Lions3", Status = "available" }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Tag", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("PetId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PetId");
|
||||
|
||||
b.ToTable("Tags");
|
||||
|
||||
b.HasData(
|
||||
new { Id = -1, Name = "Tag1", PetId = -1 },
|
||||
new { Id = -2, Name = "Tag1", PetId = -2 },
|
||||
new { Id = -3, Name = "Tag1", PetId = -3 },
|
||||
new { Id = -4, Name = "Tag1", PetId = -4 },
|
||||
new { Id = -5, Name = "Tag1", PetId = -5 },
|
||||
new { Id = -6, Name = "Tag1", PetId = -6 },
|
||||
new { Id = -7, Name = "Tag1", PetId = -7 },
|
||||
new { Id = -8, Name = "Tag1", PetId = -8 },
|
||||
new { Id = -9, Name = "Tag1", PetId = -9 },
|
||||
new { Id = -10, Name = "Tag1", PetId = -10 },
|
||||
new { Id = -11, Name = "Tag1", PetId = -11 },
|
||||
new { Id = -12, Name = "Tag1", PetId = -12 }
|
||||
);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Image", b =>
|
||||
{
|
||||
b.HasOne("BasicApi.Models.Pet")
|
||||
.WithMany("Images")
|
||||
.HasForeignKey("PetId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Pet", b =>
|
||||
{
|
||||
b.HasOne("BasicApi.Models.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BasicApi.Models.Tag", b =>
|
||||
{
|
||||
b.HasOne("BasicApi.Models.Pet")
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("PetId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BasicApi.Models
|
||||
{
|
||||
public class BasicApiContext : DbContext
|
||||
{
|
||||
public BasicApiContext(DbContextOptions options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<Category> Categories { get; set; }
|
||||
|
||||
public DbSet<Image> Images { get; set; }
|
||||
|
||||
public DbSet<Pet> Pets { get; set; }
|
||||
|
||||
public DbSet<Tag> Tags { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
var id = -1;
|
||||
var categories = new[]
|
||||
{
|
||||
new Category { Id = id--, Name = "Dogs" },
|
||||
new Category { Id = id--, Name = "Cats" },
|
||||
new Category { Id = id--, Name = "Rabbits" },
|
||||
new Category { Id = id, Name = "Lions" },
|
||||
};
|
||||
|
||||
id = -1;
|
||||
var categoryId = -1;
|
||||
var pets = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Dogs1",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Dogs2",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId--,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Dogs3",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Cats1",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Cats2",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId--,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Cats3",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Rabbits1",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Rabbits2",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId--,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Rabbits3",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Lions1",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id--,
|
||||
Name = "Lions2",
|
||||
Status = "available",
|
||||
},
|
||||
new
|
||||
{
|
||||
Age = 1,
|
||||
CategoryId = categoryId,
|
||||
HasVaccinations = true,
|
||||
Id = id,
|
||||
Name = "Lions3",
|
||||
Status = "available",
|
||||
},
|
||||
};
|
||||
|
||||
id = -1;
|
||||
var images = new[]
|
||||
{
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id--}_1.png" },
|
||||
new { Id = id, PetId = id, Url = $"http://example.com/pets/{id}_1.png" },
|
||||
};
|
||||
|
||||
id = -1;
|
||||
var tags = new[]
|
||||
{
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id--, Name = "Tag1" },
|
||||
new { Id = id, PetId = id, Name = "Tag1" },
|
||||
};
|
||||
|
||||
modelBuilder.Entity<Category>().HasData(categories);
|
||||
modelBuilder.Entity<Pet>().HasData(pets);
|
||||
modelBuilder.Entity<Image>().HasData(images);
|
||||
modelBuilder.Entity<Tag>().HasData(tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace BasicApi.Models
|
||||
{
|
||||
public class Category
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace BasicApi.Models
|
||||
{
|
||||
public class Image
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BasicApi.Models
|
||||
{
|
||||
public class Pet
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[Range(0, 150)]
|
||||
public int Age { get; set; }
|
||||
|
||||
public Category Category { get; set; }
|
||||
|
||||
public bool HasVaccinations { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(50, MinimumLength = 2)]
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
public List<Tag> Tags { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Status { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace BasicApi.Models
|
||||
{
|
||||
public class Tag
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
using System.Linq;
|
||||
#endif
|
||||
using System.Security.Cryptography;
|
||||
using BasicApi.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Npgsql;
|
||||
|
||||
namespace BasicApi
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private bool _isSQLite;
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var rsa = new RSACryptoServiceProvider(2048);
|
||||
var key = new RsaSecurityKey(rsa.ExportParameters(true));
|
||||
|
||||
services.AddSingleton(new SigningCredentials(
|
||||
key,
|
||||
SecurityAlgorithms.RsaSha256Signature));
|
||||
|
||||
services.AddAuthentication().AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters.IssuerSigningKey = key;
|
||||
options.TokenValidationParameters.ValidAudience = "Myself";
|
||||
options.TokenValidationParameters.ValidIssuer = "BasicApi";
|
||||
});
|
||||
|
||||
var connectionString = Configuration["ConnectionString"];
|
||||
var databaseType = Configuration["Database"];
|
||||
if (string.IsNullOrEmpty(databaseType))
|
||||
{
|
||||
// Use SQLite when running outside a benchmark test or if benchmarks user specified "None".
|
||||
// ("None" is not passed to the web application.)
|
||||
databaseType = "SQLite";
|
||||
}
|
||||
else if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
throw new ArgumentException("Connection string must be specified for {databaseType}.");
|
||||
}
|
||||
|
||||
switch (databaseType.ToUpper())
|
||||
{
|
||||
#if !NET461
|
||||
case "MYSQL":
|
||||
services
|
||||
.AddEntityFrameworkMySql()
|
||||
.AddDbContext<BasicApiContext>(options => options.UseMySql(connectionString));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case "POSTGRESQL":
|
||||
var settings = new NpgsqlConnectionStringBuilder(connectionString);
|
||||
if (!settings.NoResetOnClose)
|
||||
{
|
||||
throw new ArgumentException("No Reset On Close=true must be specified for Npgsql.");
|
||||
}
|
||||
if (settings.Enlist)
|
||||
{
|
||||
throw new ArgumentException("Enlist=false must be specified for Npgsql.");
|
||||
}
|
||||
|
||||
services
|
||||
.AddEntityFrameworkNpgsql()
|
||||
.AddDbContextPool<BasicApiContext>(options => options.UseNpgsql(connectionString));
|
||||
break;
|
||||
|
||||
case "SQLITE":
|
||||
_isSQLite = true;
|
||||
services
|
||||
.AddEntityFrameworkSqlite()
|
||||
.AddDbContextPool<BasicApiContext>(options => options.UseSqlite("Data Source=BasicApi.db"));
|
||||
break;
|
||||
|
||||
case "SQLSERVER":
|
||||
services
|
||||
.AddEntityFrameworkSqlServer()
|
||||
.AddDbContextPool<BasicApiContext>(options => options.UseSqlServer(connectionString));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Application does not support database type {databaseType}.");
|
||||
}
|
||||
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(
|
||||
"pet-store-reader",
|
||||
builder => builder
|
||||
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim("scope", "pet-store-reader"));
|
||||
|
||||
options.AddPolicy(
|
||||
"pet-store-writer",
|
||||
builder => builder
|
||||
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim("scope", "pet-store-writer"));
|
||||
});
|
||||
|
||||
services
|
||||
.AddMvcCore()
|
||||
.AddAuthorization()
|
||||
.AddJsonFormatters(json => json.ContractResolver = new CamelCasePropertyNamesContractResolver())
|
||||
.AddDataAnnotations();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime)
|
||||
{
|
||||
var services = app.ApplicationServices;
|
||||
CreateDatabaseTables(services);
|
||||
if (_isSQLite)
|
||||
{
|
||||
lifetime.ApplicationStopping.Register(() => DropDatabase(services));
|
||||
}
|
||||
else
|
||||
{
|
||||
lifetime.ApplicationStopping.Register(() => DropDatabaseTables(services));
|
||||
}
|
||||
|
||||
app.Use(next => async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await next(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseMvc();
|
||||
}
|
||||
|
||||
private void CreateDatabaseTables(IServiceProvider services)
|
||||
{
|
||||
using (var serviceScope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<BasicApiContext>())
|
||||
{
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
var migrator = dbContext.GetService<IMigrator>();
|
||||
var script = migrator.GenerateScript(
|
||||
fromMigration: Migration.InitialDatabase,
|
||||
toMigration: dbContext.Database.GetMigrations().LastOrDefault());
|
||||
Console.WriteLine("Create script:");
|
||||
Console.WriteLine(script);
|
||||
#endif
|
||||
|
||||
dbContext.Database.Migrate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't leave SQLite's .db file behind.
|
||||
public static void DropDatabase(IServiceProvider services)
|
||||
{
|
||||
using (var serviceScope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<BasicApiContext>())
|
||||
{
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
var migrator = dbContext.GetService<IMigrator>();
|
||||
var script = migrator.GenerateScript(
|
||||
fromMigration: dbContext.Database.GetAppliedMigrations().LastOrDefault(),
|
||||
toMigration: Migration.InitialDatabase);
|
||||
Console.WriteLine("Delete script:");
|
||||
Console.WriteLine(script);
|
||||
#endif
|
||||
|
||||
dbContext.Database.EnsureDeleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DropDatabaseTables(IServiceProvider services)
|
||||
{
|
||||
using (var serviceScope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<BasicApiContext>())
|
||||
{
|
||||
var migrator = dbContext.GetService<IMigrator>();
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
var script = migrator.GenerateScript(
|
||||
fromMigration: dbContext.Database.GetAppliedMigrations().LastOrDefault(),
|
||||
toMigration: Migration.InitialDatabase);
|
||||
Console.WriteLine("Delete script:");
|
||||
Console.WriteLine(script);
|
||||
#endif
|
||||
|
||||
migrator.Migrate(Migration.InitialDatabase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = CreateWebHostBuilder(args)
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
|
||||
return new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseUrls("http://+:5000")
|
||||
.UseConfiguration(configuration)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"Default": {
|
||||
"Client": "Wrk",
|
||||
"Headers": {
|
||||
"Cache-Control": "no-cache"
|
||||
},
|
||||
"PresetHeaders": "Json",
|
||||
"ReadyStateText": "Application started.",
|
||||
"Source": {
|
||||
"BranchOrCommit": "dev",
|
||||
"Project": "benchmarkapps/BasicApi/BasicApi.csproj",
|
||||
"Repository": "https://github.com/aspnet/mvc.git"
|
||||
}
|
||||
},
|
||||
"BasicApi.GetToken": {
|
||||
"Path": "/token",
|
||||
"PresetHeaders": "Plaintext",
|
||||
"Query": "?username=reader@example.com"
|
||||
},
|
||||
"BasicApi.GetUsingQueryString": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicApi/getWithToken.lua"
|
||||
},
|
||||
"Path": "/pet/findByStatus",
|
||||
"Query": "?status=available"
|
||||
},
|
||||
"BasicApi.GetUsingRouteValue": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicApi/getWithToken.lua"
|
||||
},
|
||||
"Path": "/pet/-1"
|
||||
},
|
||||
"BasicApi.GetUsingRouteValueWithoutAuthorization": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicApi/getWithToken.lua"
|
||||
},
|
||||
"Path": "/pet/anonymous/-1"
|
||||
},
|
||||
"BasicApi.GetUsingRouteValueWithoutToken": {
|
||||
"Path": "/pet/anonymous/-1"
|
||||
},
|
||||
"BasicApi.Post": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicApi/postJsonWithToken.lua"
|
||||
},
|
||||
"Path": "/pet"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
-- script that retrieves an authentication token to send in all future requests
|
||||
-- keep this file and postJsonWithToken.lua in sync with respect to token handling
|
||||
|
||||
-- use token for at most maxRequests, default throughout test
|
||||
local counter = 0
|
||||
local maxRequests = -1
|
||||
|
||||
-- request access necessary for both reading and writing by default
|
||||
local username = "writer@example.com"
|
||||
|
||||
-- marker that we have completed the first request
|
||||
local token = nil
|
||||
|
||||
function init(args)
|
||||
if args[1] ~= nil then
|
||||
maxRequests = args[1]
|
||||
print("Max requests: " .. maxRequests)
|
||||
end
|
||||
if args[2] ~= nil then
|
||||
username = args[2]
|
||||
end
|
||||
|
||||
local path = "/token?username=" .. username
|
||||
|
||||
-- initialize first (empty) request
|
||||
req = wrk.format("GET", path, nil, "")
|
||||
end
|
||||
|
||||
function request()
|
||||
return req
|
||||
end
|
||||
|
||||
function response(status, headers, body)
|
||||
if not token and status == 200 then
|
||||
token = body
|
||||
wrk.headers["Authorization"] = "Bearer " .. token
|
||||
req = wrk.format()
|
||||
return
|
||||
end
|
||||
|
||||
if not token then
|
||||
print("Failed initial request! status: " .. status)
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
if counter == maxRequests then
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
counter = counter + 1
|
||||
end
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
-- script that retrieves an authentication token to send in all future requests and adds a body for those requests
|
||||
-- keep this file and getWithToken.lua in sync with respect to token handling
|
||||
|
||||
-- do not use wrk's default request
|
||||
local req = nil
|
||||
|
||||
-- use token for at most maxRequests, default throughout test
|
||||
local counter = 0
|
||||
local maxRequests = -1
|
||||
|
||||
-- request access necessary for both reading and writing by default
|
||||
local username = "writer@example.com"
|
||||
|
||||
-- marker that we have completed the first request
|
||||
local token = nil
|
||||
|
||||
function init(args)
|
||||
if args[1] ~= nil then
|
||||
maxRequests = args[1]
|
||||
print("Max requests: " .. maxRequests)
|
||||
end
|
||||
if args[2] ~= nil then
|
||||
username = args[2]
|
||||
end
|
||||
|
||||
local path = "/token?username=" .. username
|
||||
|
||||
-- initialize first (empty) request
|
||||
req = wrk.format("GET", path, nil, "")
|
||||
end
|
||||
|
||||
function request()
|
||||
return req
|
||||
end
|
||||
|
||||
function response(status, headers, body)
|
||||
if not token and status == 200 then
|
||||
token = body
|
||||
wrk.headers["Authorization"] = "Bearer " .. token
|
||||
wrk.headers["Content-Type"] = "application/json"
|
||||
wrk.method = "POST"
|
||||
wrk.body = [[
|
||||
{
|
||||
"category": {
|
||||
"name": "Cats"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"url": "http://example.com/images/fluffy1.png"
|
||||
},
|
||||
{
|
||||
"url": "http://example.com/images/fluffy2.png"
|
||||
},
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "orange"
|
||||
},
|
||||
{
|
||||
"name": "kitty"
|
||||
}
|
||||
],
|
||||
"age": 2,
|
||||
"hasVaccinations": "true",
|
||||
"name": "fluffy",
|
||||
"status": "available"
|
||||
}]]
|
||||
|
||||
req = wrk.format()
|
||||
return
|
||||
end
|
||||
|
||||
if not token then
|
||||
print("Failed initial request! status: " .. status)
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
if counter == maxRequests then
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
counter = counter + 1
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"configProperties": {
|
||||
"System.GC.Server": true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(BenchmarksTargetFramework)' != ''">$(BenchmarksTargetFramework)</TargetFrameworks>
|
||||
|
||||
<DefineConstants Condition=" '$(GenerateSqlScripts)'=='true' ">$(DefineConstants);GENERATE_SQL_SCRIPTS</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);__RemoveThisBitTo__GENERATE_SQL_SCRIPTS</DefineConstants>
|
||||
|
||||
<WarningsNotAsErrors>CS8002;$(WarningsNotAsErrors)</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(BenchmarksOnlyNpgsqlEntityFrameworkCorePostgreSQLPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net461'">
|
||||
<PackageReference Include="MySqlConnector" Version="$(BenchmarksOnlyMySqlConnectorPackageVersion)" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="$(BenchmarksOnlyPomeloEntityFrameworkCoreMySqlPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- These references are used when running locally -->
|
||||
<ItemGroup Condition="'$(BenchmarksTargetFramework)' == ''">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="$(BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion)" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlitePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlServerPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
These references are used when running on the Benchmarks Server.
|
||||
Use All meta-package and not App to include Microsoft.EntityFrameworkCore.Sqlite.
|
||||
-->
|
||||
<ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(MicrosoftAspNetCoreAllPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BasicViews
|
||||
{
|
||||
public class BasicViewsContext : DbContext
|
||||
{
|
||||
public BasicViewsContext(DbContextOptions options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<Person> People { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BasicViews.Components
|
||||
{
|
||||
public class CurrentUser : ViewComponent
|
||||
{
|
||||
private static readonly string[] Names = { "Curly", "Curly Joe", "Joe", "Larry", "Moe", "Shemp" };
|
||||
private static int index = 0;
|
||||
|
||||
public string Invoke()
|
||||
{
|
||||
index = index++ / Names.Length;
|
||||
return Names[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BasicViews.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly BasicViewsContext _context;
|
||||
|
||||
public HomeController(BasicViewsContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Index(Person person)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
_context.Add(person);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return View(person);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult IndexWithoutToken()
|
||||
{
|
||||
return View(viewName: nameof(Index));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public async Task<IActionResult> IndexWithoutToken(Person person)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
_context.Add(person);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return View(viewName: nameof(Index), model: person);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult HtmlHelpers()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> HtmlHelpers(Person person)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
_context.Add(person);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return View(person);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
benchmarkapps/BasicViews/Migrations/20180609000611_InitialCreate.Designer.cs
generated
Normal file
44
benchmarkapps/BasicViews/Migrations/20180609000611_InitialCreate.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace BasicViews.Migrations
|
||||
{
|
||||
[DbContext(typeof(BasicViewsContext))]
|
||||
[Migration("20180609000611_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "2.1.0-rtm-30799")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("BasicViews.Person", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("Age");
|
||||
|
||||
b.Property<DateTimeOffset>("BirthDate");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(27);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("People");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace BasicViews.Migrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "People",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
#if !NET461
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
|
||||
#endif
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(maxLength: 27, nullable: true),
|
||||
Age = table.Column<int>(nullable: false),
|
||||
BirthDate = table.Column<DateTimeOffset>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_People", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "People");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace BasicViews.Migrations
|
||||
{
|
||||
[DbContext(typeof(BasicViewsContext))]
|
||||
partial class BasicViewsContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "2.1.0-rtm-30799")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("BasicViews.Person", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("Age");
|
||||
|
||||
b.Property<DateTimeOffset>("BirthDate");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(27);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("People");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BasicViews
|
||||
{
|
||||
public class Person
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[StringLength(27, MinimumLength = 2)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Range(10, 54)]
|
||||
public int Age { get; set; }
|
||||
|
||||
public DateTimeOffset BirthDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
using System.Linq;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Npgsql;
|
||||
|
||||
namespace BasicViews
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private bool _isSQLite;
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var connectionString = Configuration["ConnectionString"];
|
||||
var databaseType = Configuration["Database"];
|
||||
if (string.IsNullOrEmpty(databaseType))
|
||||
{
|
||||
// Use SQLite when running outside a benchmark test or if benchmarks user specified "None".
|
||||
// ("None" is not passed to the web application.)
|
||||
databaseType = "SQLite";
|
||||
}
|
||||
else if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
throw new ArgumentException("Connection string must be specified for {databaseType}.");
|
||||
}
|
||||
|
||||
switch (databaseType.ToUpper())
|
||||
{
|
||||
#if !NET461
|
||||
case "MYSQL":
|
||||
services
|
||||
.AddEntityFrameworkMySql()
|
||||
.AddDbContext<BasicViewsContext>(options => options.UseMySql(connectionString));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case "POSTGRESQL":
|
||||
var settings = new NpgsqlConnectionStringBuilder(connectionString);
|
||||
if (!settings.NoResetOnClose)
|
||||
{
|
||||
throw new ArgumentException("No Reset On Close=true must be specified for Npgsql.");
|
||||
}
|
||||
if (settings.Enlist)
|
||||
{
|
||||
throw new ArgumentException("Enlist=false must be specified for Npgsql.");
|
||||
}
|
||||
|
||||
services
|
||||
.AddEntityFrameworkNpgsql()
|
||||
.AddDbContextPool<BasicViewsContext>(options => options.UseNpgsql(connectionString));
|
||||
break;
|
||||
|
||||
case "SQLITE":
|
||||
_isSQLite = true;
|
||||
services
|
||||
.AddEntityFrameworkSqlite()
|
||||
.AddDbContextPool<BasicViewsContext>(options => options.UseSqlite("Data Source=BasicViews.db"));
|
||||
break;
|
||||
|
||||
case "SQLSERVER":
|
||||
services
|
||||
.AddEntityFrameworkSqlServer()
|
||||
.AddDbContextPool<BasicViewsContext>(options => options.UseSqlServer(connectionString));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Application does not support database type {databaseType}.");
|
||||
}
|
||||
|
||||
services.AddMvc();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime)
|
||||
{
|
||||
var services = app.ApplicationServices;
|
||||
CreateDatabaseTables(services);
|
||||
if (_isSQLite)
|
||||
{
|
||||
lifetime.ApplicationStopping.Register(() => DropDatabase(services));
|
||||
}
|
||||
else
|
||||
{
|
||||
lifetime.ApplicationStopping.Register(() => DropDatabaseTables(services));
|
||||
}
|
||||
|
||||
app.Use(next => async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await next(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseMvcWithDefaultRoute();
|
||||
}
|
||||
|
||||
private void CreateDatabaseTables(IServiceProvider services)
|
||||
{
|
||||
using (var serviceScope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<BasicViewsContext>())
|
||||
{
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
var migrator = dbContext.GetService<IMigrator>();
|
||||
var script = migrator.GenerateScript(
|
||||
fromMigration: Migration.InitialDatabase,
|
||||
toMigration: dbContext.Database.GetMigrations().LastOrDefault());
|
||||
Console.WriteLine("Create script:");
|
||||
Console.WriteLine(script);
|
||||
#endif
|
||||
|
||||
dbContext.Database.Migrate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't leave SQLite's .db file behind.
|
||||
public static void DropDatabase(IServiceProvider services)
|
||||
{
|
||||
using (var serviceScope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<BasicViewsContext>())
|
||||
{
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
var migrator = dbContext.GetService<IMigrator>();
|
||||
var script = migrator.GenerateScript(
|
||||
fromMigration: dbContext.Database.GetAppliedMigrations().LastOrDefault(),
|
||||
toMigration: Migration.InitialDatabase);
|
||||
Console.WriteLine("Delete script:");
|
||||
Console.WriteLine(script);
|
||||
#endif
|
||||
|
||||
dbContext.Database.EnsureDeleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DropDatabaseTables(IServiceProvider services)
|
||||
{
|
||||
using (var serviceScope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<BasicViewsContext>())
|
||||
{
|
||||
var migrator = dbContext.GetService<IMigrator>();
|
||||
#if GENERATE_SQL_SCRIPTS
|
||||
var script = migrator.GenerateScript(
|
||||
fromMigration: dbContext.Database.GetAppliedMigrations().LastOrDefault(),
|
||||
toMigration: Migration.InitialDatabase);
|
||||
Console.WriteLine("Delete script:");
|
||||
Console.WriteLine(script);
|
||||
#endif
|
||||
|
||||
migrator.Migrate(Migration.InitialDatabase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = CreateWebHostBuilder(args)
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
|
||||
return new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseUrls("http://+:5000")
|
||||
.UseConfiguration(configuration)
|
||||
.UseIISIntegration()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
@using BasicViews
|
||||
@model Person
|
||||
|
||||
@Html.ValidationSummary()
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
<div>
|
||||
@Html.LabelFor(p => p.Name)
|
||||
@Html.EditorFor(p => p.Name)
|
||||
</div>
|
||||
<div>
|
||||
@Html.LabelFor(p => p.Age)
|
||||
@Html.EditorFor(p => p.Age)
|
||||
</div>
|
||||
<div>
|
||||
@Html.LabelFor(p => p.BirthDate)
|
||||
@Html.EditorFor(p => p.BirthDate)
|
||||
</div>
|
||||
<input type="submit" />
|
||||
|
||||
@Html.AntiForgeryToken()
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
@using BasicViews
|
||||
@model Person
|
||||
|
||||
<div asp-validation-summary="All">
|
||||
</div>
|
||||
|
||||
<form asp-action="Index" asp-controller="Home">
|
||||
<div>
|
||||
<label asp-for="Name"></label>
|
||||
<input asp-for="Name" />
|
||||
</div>
|
||||
<div>
|
||||
<label asp-for="Age"></label>
|
||||
<input asp-for="Age" />
|
||||
</div>
|
||||
<div>
|
||||
<label asp-for="BirthDate"></label>
|
||||
<input asp-for="BirthDate" />
|
||||
</div>
|
||||
<input type="submit" />
|
||||
</form>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>MVC with views</title>
|
||||
|
||||
<environment names="Development">
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
</environment>
|
||||
<environment names="Staging,Production">
|
||||
<link rel="stylesheet" href="~/css/site.min.css" asp-fallback-href="~/css/site.css"
|
||||
asp-fallback-test-class="test-it" asp-fallback-test-property="float"
|
||||
asp-fallback-test-value="right" asp-append-version="true" />
|
||||
</environment>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a asp-controller="Home" asp-action="Index" class="navbar-brand">BasicViews</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a asp-controller="Home" asp-action="Index">TagHelpers</a></li>
|
||||
<li><a asp-controller="Home" asp-action="HtmlHelpers">HtmlHelpers</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container body-content">
|
||||
<p>Hello @await Component.InvokeAsync("CurrentUser")!</p>
|
||||
|
||||
<div>
|
||||
@RenderBody()
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<environment names="Development">
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
</environment>
|
||||
<environment names="Staging,Production">
|
||||
<script src="~/js/site.min.js" asp-fallback-src="~/js/site.js"
|
||||
asp-fallback-test="test" asp-append-version="true"></script>
|
||||
</environment>
|
||||
|
||||
@RenderSection("scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"Default": {
|
||||
"Client": "Wrk",
|
||||
"Headers": {
|
||||
"Cache-Control": "no-cache"
|
||||
},
|
||||
"PresetHeaders": "Html",
|
||||
"ReadyStateText": "Application started.",
|
||||
"Source": {
|
||||
"BranchOrCommit": "dev",
|
||||
"Project": "benchmarkapps/BasicViews/BasicViews.csproj",
|
||||
"Repository": "https://github.com/aspnet/mvc.git"
|
||||
}
|
||||
},
|
||||
"BasicViews.GetHtmlHelpers": {
|
||||
"Path": "/Home/HtmlHelpers"
|
||||
},
|
||||
"BasicViews.GetTagHelpers": {
|
||||
"Path": "/Home/Index"
|
||||
},
|
||||
"BasicViews.Post": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicViews/postWithToken.lua"
|
||||
},
|
||||
"Path": "/Home/Index"
|
||||
},
|
||||
"BasicViews.PostIgnoringToken": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicViews/postWithToken.lua"
|
||||
},
|
||||
"Path": "/Home/IndexWithoutToken"
|
||||
},
|
||||
"BasicViews.PostWithoutToken": {
|
||||
"ClientProperties": {
|
||||
"Scripts": "https://raw.githubusercontent.com/aspnet/Mvc/dev/benchmarkapps/BasicViews/post.lua"
|
||||
},
|
||||
"Path": "/Home/IndexWithoutToken"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
-- script that POSTs body for requests
|
||||
|
||||
function init(args)
|
||||
wrk.body = "Age=12&BirthDate=2006-03-01T09%3A51%3A43.041-07%3A00&Name=George"
|
||||
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
wrk.method = "POST"
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
-- script that retrieves an antiforgery token to send in all future requests and adds a body for those requests
|
||||
|
||||
-- do not use wrk's default request
|
||||
local req = nil
|
||||
|
||||
-- use token for at most maxRequests, default throughout test
|
||||
local counter = 0
|
||||
local maxRequests = -1
|
||||
|
||||
-- marker that we have completed the first request
|
||||
local token = nil
|
||||
|
||||
function init(args)
|
||||
-- initialize first (empty) request
|
||||
req = wrk.format("GET")
|
||||
end
|
||||
|
||||
function request()
|
||||
return req
|
||||
end
|
||||
|
||||
function response(status, headers, body)
|
||||
if not token and status == 200 then
|
||||
local cookie = string.gsub(headers["Set-Cookie"], "^([^;]*)(;.*)?$", "%1")
|
||||
if not cookie or cookie == "" then
|
||||
print("Unable to find antiforgery cookie in initial response!")
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
token = string.gsub(body, '^.* name="__RequestVerificationToken".* value="([^"]*)"[ >].*$', "%1")
|
||||
if not token or token == "" then
|
||||
print("Unable to find antiforgery token in initial response!")
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
wrk.body = "Age=12&BirthDate=2006-03-01T09%3A51%3A43.041-07%3A00&Name=George&__RequestVerificationToken=" .. token
|
||||
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
wrk.headers["Cookie"] = cookie
|
||||
wrk.method = "POST"
|
||||
|
||||
req = wrk.format()
|
||||
return
|
||||
end
|
||||
|
||||
if not token then
|
||||
print("Failed initial request! status: " .. status)
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
if counter == maxRequests then
|
||||
wrk.thread:stop()
|
||||
end
|
||||
|
||||
counter = counter + 1
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"configProperties": {
|
||||
"System.GC.Server": true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
|
||||
</handlers>
|
||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" stdoutLogEnabled="false" />
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
label {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.test-it {
|
||||
float: right;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
label {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.test-it {
|
||||
float: right;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
console.log("Hello World");
|
||||
function test() {
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
console.log("Hello Minified World");
|
||||
function test() {
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
## Purpose
|
||||
|
||||
These projects assist in Benchmarking MVC.
|
||||
They makes it easier to test local changes than having the App in the Benchmarks repo by letting us make changes in MVC branches and use the example commandline below to run the benchmarks against our branches.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Push changes you would like to test to a branch on GitHub
|
||||
2. Clone aspnet/benchmarks repo to your machine or install the global BenchmarksDriver tool https://www.nuget.org/packages/BenchmarksDriver/
|
||||
3. If cloned go to the BenchmarksDriver project
|
||||
4. Use the following command as a guideline for running a test using your changes
|
||||
|
||||
`benchmarks --server <server-endpoint> --client <client-endpoint> -j https://raw.githubusercontent.com/aspnet/MVC/dev/benchmarkaps/BasicApi/BasicApi.json`
|
||||
|
||||
5. For more info/commands see https://github.com/aspnet/benchmarks/blob/dev/src/BenchmarksDriver/README.md
|
||||
|
|
@ -5,12 +5,27 @@
|
|||
<PropertyGroup Label="Package Versions">
|
||||
<AngleSharpPackageVersion>0.9.9</AngleSharpPackageVersion>
|
||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||
|
||||
<!--
|
||||
BenchmarksOnly* package versions come from NuGet.org and are intended only for use in benchmarks apps where EF
|
||||
is not otherwise referenced. They avoid unnecessary changes to the Universe build graph or to product
|
||||
dependencies. Do not use these properties elsewhere.
|
||||
-->
|
||||
<BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>2.1.0</BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>
|
||||
<BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlitePackageVersion>2.1.0</BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlitePackageVersion>
|
||||
<BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.0</BenchmarksOnlyMicrosoftEntityFrameworkCoreSqlServerPackageVersion>
|
||||
<BenchmarksOnlyMySqlConnectorPackageVersion>0.42.1</BenchmarksOnlyMySqlConnectorPackageVersion>
|
||||
<BenchmarksOnlyNpgsqlEntityFrameworkCorePostgreSQLPackageVersion>2.1.0</BenchmarksOnlyNpgsqlEntityFrameworkCorePostgreSQLPackageVersion>
|
||||
<BenchmarksOnlyPomeloEntityFrameworkCoreMySqlPackageVersion>2.1.0-rc1-final</BenchmarksOnlyPomeloEntityFrameworkCoreMySqlPackageVersion>
|
||||
|
||||
<InternalAspNetCoreAnalyzersPackageVersion>2.2.0-preview1-34492</InternalAspNetCoreAnalyzersPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview1-17090</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAllPackageVersion>
|
||||
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreAntiforgeryPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAntiforgeryPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAuthenticationPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-preview1-34492</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
|
|
@ -51,6 +66,7 @@
|
|||
<MicrosoftDiaSymReaderNativePackageVersion>1.7.0</MicrosoftDiaSymReaderNativePackageVersion>
|
||||
<MicrosoftExtensionsCachingMemoryPackageVersion>2.2.0-preview1-34492</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.2.0-preview1-34492</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview1-34492</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34492</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationPackageVersion>2.2.0-preview1-34492</MicrosoftExtensionsConfigurationPackageVersion>
|
||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.2.0-preview1-34492</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public class BasicApiTest : IClassFixture<BasicApiFixture>
|
||||
{
|
||||
private static readonly byte[] PetBytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
|
||||
.GetBytes(@"{
|
||||
""category"" : {
|
||||
""name"" : ""Cats""
|
||||
},
|
||||
""images"": [
|
||||
{
|
||||
""url"": ""http://example.com/images/fluffy1.png""
|
||||
},
|
||||
{
|
||||
""url"": ""http://example.com/images/fluffy2.png""
|
||||
},
|
||||
],
|
||||
""tags"": [
|
||||
{
|
||||
""name"": ""orange""
|
||||
},
|
||||
{
|
||||
""name"": ""kitty""
|
||||
}
|
||||
],
|
||||
""age"": 2,
|
||||
""hasVaccinations"": ""true"",
|
||||
""name"" : ""fluffy"",
|
||||
""status"" : ""available""
|
||||
}");
|
||||
|
||||
public BasicApiTest(BasicApiFixture fixture)
|
||||
{
|
||||
Client = fixture.CreateClient();
|
||||
}
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Fact]
|
||||
public async Task Token_WithUnknownUser_ReturnsForbidden()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync("/token?username=fallguy@example.com");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Token_WithKnownUser_ReturnsOkAndToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync("/token?username=reader@example.com");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.MediaType);
|
||||
|
||||
var token = await response.Content.ReadAsStringAsync();
|
||||
Assert.NotNull(token);
|
||||
Assert.NotEmpty(token);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FindByStatus_WithNoToken_ReturnsUnauthorized()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync("/pet/findByStatus?status=available");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("reader@example.com")]
|
||||
[InlineData("writer@example.com")]
|
||||
public async Task FindByStatus_WithToken_ReturnsOkAndPet(string username)
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var token = await Client.GetStringAsync($"/token?username={username}");
|
||||
|
||||
// Assert 1 (guard)
|
||||
Assert.NotEmpty(token);
|
||||
|
||||
// Arrange 2
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "/pet/findByStatus?status=available");
|
||||
request.Headers.Add(HeaderNames.Authorization, $"Bearer {token}");
|
||||
|
||||
// Act 2
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert 2
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
Assert.NotNull(json);
|
||||
Assert.NotEmpty(json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FindById_WithInvalidPetId_ReturnsNotFound()
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var token = await Client.GetStringAsync("/token?username=reader@example.com");
|
||||
|
||||
// Assert 1 (guard)
|
||||
Assert.NotEmpty(token);
|
||||
|
||||
// Arrange 2
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "/pet/100");
|
||||
request.Headers.Add(HeaderNames.Authorization, $"Bearer {token}");
|
||||
|
||||
// Act 2
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert 2
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FindById_WithValidPetId_ReturnsOkAndPet()
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var token = await Client.GetStringAsync("/token?username=reader@example.com");
|
||||
|
||||
// Assert 1 (guard)
|
||||
Assert.NotEmpty(token);
|
||||
|
||||
// Arrange 2
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "/pet/-1");
|
||||
request.Headers.Add(HeaderNames.Authorization, $"Bearer {token}");
|
||||
|
||||
// Act 2
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert 2
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
Assert.NotNull(json);
|
||||
Assert.NotEmpty(json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddPet_WithInsufficientClaims_ReturnsForbidden()
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var token = await Client.GetStringAsync("/token?username=reader@example.com");
|
||||
|
||||
// Assert 1 (guard)
|
||||
Assert.NotEmpty(token);
|
||||
|
||||
// Arrange 2
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "/pet")
|
||||
{
|
||||
Content = new ByteArrayContent(PetBytes)
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
{ "Content-Type", "application/json" },
|
||||
},
|
||||
},
|
||||
Headers =
|
||||
{
|
||||
{ HeaderNames.Authorization, $"Bearer {token}" },
|
||||
},
|
||||
};
|
||||
|
||||
// Act 2
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert 2
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddPet_WithValidClaims_ReturnsCreated()
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var token = await Client.GetStringAsync("/token?username=writer@example.com");
|
||||
|
||||
// Assert 1 (guard)
|
||||
Assert.NotEmpty(token);
|
||||
|
||||
// Arrange 2
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "/pet")
|
||||
{
|
||||
Content = new ByteArrayContent(PetBytes)
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
{ HeaderNames.ContentType, "application/json" },
|
||||
},
|
||||
},
|
||||
Headers =
|
||||
{
|
||||
{ HeaderNames.Authorization, $"Bearer {token}" },
|
||||
},
|
||||
};
|
||||
|
||||
// Act 2
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert 2
|
||||
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
|
||||
var location = response.Headers.Location.ToString();
|
||||
Assert.NotNull(location);
|
||||
Assert.EndsWith("/1", location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public class BasicViewsTest : IClassFixture<BasicViewsFixture>
|
||||
{
|
||||
public BasicViewsTest(BasicViewsFixture fixture)
|
||||
{
|
||||
Client = fixture.CreateClient();
|
||||
}
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Theory]
|
||||
[InlineData("/")]
|
||||
[InlineData("/Home/HtmlHelpers")]
|
||||
public async Task Get_ReturnsOkAndAntiforgeryToken(string path)
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetAsync(path);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("text/html", response.Content.Headers.ContentType.MediaType);
|
||||
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
Assert.NotNull(html);
|
||||
Assert.NotEmpty(html);
|
||||
|
||||
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(html, "/");
|
||||
Assert.NotNull(token);
|
||||
Assert.NotEmpty(token);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/")]
|
||||
[InlineData("/Home/HtmlHelpers")]
|
||||
public async Task Post_ReturnsOkAndNewPerson(string path)
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var html = await Client.GetStringAsync(path);
|
||||
|
||||
// Assert 1 (guard)
|
||||
Assert.NotEmpty(html);
|
||||
|
||||
// Arrange 2
|
||||
var token = AntiforgeryTestHelper.RetrieveAntiforgeryToken(html, "/");
|
||||
var name = Guid.NewGuid().ToString();
|
||||
name = name.Substring(startIndex: 0, length: name.LastIndexOf('-'));
|
||||
var form = new Dictionary<string, string>
|
||||
{
|
||||
{ "__RequestVerificationToken", token },
|
||||
{ "Age", "12" },
|
||||
{ "BirthDate", "2006-03-01T09:51:43.041-07:00" },
|
||||
{ "Name", name },
|
||||
};
|
||||
|
||||
var content = new FormUrlEncodedContent(form);
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, path)
|
||||
{
|
||||
Content = content,
|
||||
};
|
||||
|
||||
// Act 2
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert 2
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.NotNull(body);
|
||||
Assert.Contains($@"value=""{name}""", body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BasicApi;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public class BasicApiFixture : MvcTestFixture<Startup>
|
||||
{
|
||||
// Do not leave .db file behind. Also, ensure added pet gets expected id (1) in subsequent runs.
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Startup.DropDatabase(Server.Host.Services);
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BasicViews;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public class BasicViewsFixture : MvcTestFixture<Startup>
|
||||
{
|
||||
// Do not leave .db file behind.
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Startup.DropDatabase(Server.Host.Services);
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Testing\Microsoft.AspNetCore.Mvc.Testing.csproj" />
|
||||
<ProjectReference Include="..\..\benchmarkapps\BasicApi\BasicApi.csproj" />
|
||||
<ProjectReference Include="..\..\benchmarkapps\BasicViews\BasicViews.csproj" />
|
||||
<ProjectReference Include="..\..\samples\MvcSandbox\MvcSandbox.csproj" />
|
||||
|
||||
<ProjectReference Include="..\WebSites\ApiExplorerWebSite\ApiExplorerWebSite.csproj" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue