かきスタンプ

福岡でフリーランスの物流系のエンジニアやってます。

Visual Studio 2019 Launch Event in Fukuoka振り返り:ハンズオンの Visual Studio 2019 使用バージョン

こちらのイベントに参加させて頂きました。
fukuten.connpass.com

Visual Studio をはじめとした Microsoft の最新情報を届けてくれると同時に、ハンズオンまであるという濃いイベント。
 
ハンズオンにて紹介している資料では、Azure Cloud Shell を使用していますが、せっかくなので、Visual Studio 2019 でやってみました。
 
なお、ハンズオン資料は以下です。
Visual Studio 2019 Launch Event in Fukuoka

ASP.NET Core を使用して Web API を構築する - Learn | Microsoft Docs

1.新しいプロジェクトの作成

f:id:kakisoft:20190416015035p:plain

2.ASP.NET Core Webアプリケーション

f:id:kakisoft:20190416015046p:plain

3.プロジェクト名を入力

プロジェクト名は、チュートリアル同様「RetailApi」としました。
f:id:kakisoft:20190416015058p:plain

4.「API」を選択

f:id:kakisoft:20190416015112p:plain

5.実行

▶ボタンを押すと、Webサーバが起動します。
f:id:kakisoft:20190416015124p:plain

f:id:kakisoft:20190416015137p:plain

6.フォルダを追加

一旦、Webサーバを停止させます。
フォルダを追加します。RetailApiの階層にて右クリックし、フォルダを追加。

f:id:kakisoft:20190416015155p:plain

7.

以下の3つのフォルダを作成します。

  • Controllers
  • Data
  • Models

f:id:kakisoft:20190416015215p:plain

8.ファイルを追加

ファイルを追加します。
追加する階層にて右クリックし、「新しい項目」を選択。

f:id:kakisoft:20190416015227p:plain

9.

コードファイルを選択し、作成。

f:id:kakisoft:20190416015237p:plain

10.

最終的にこんな感じになります。

f:id:kakisoft:20190416015247p:plain

追加・編集するコードは以下のようになります。

Controllers/ProductsController.cs

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RetailApi.Data;
using RetailApi.Models;

namespace RetailApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly ProductsContext _context;

        public ProductsController(ProductsContext context)
        {
            _context = context;
        }

        [HttpGet]
        public ActionResult<List<Product>> GetAll() =>
            _context.Products.ToList();

        // GET by ID action
        [HttpGet("{id}")]
        public async Task<ActionResult<Product>> GetById(long id)
        {
            var product = await _context.Products.FindAsync(id);

            if (product == null)
            {
                return NotFound();
            }

            return product;
        }

        // POST action
        [HttpPost]
        public async Task<ActionResult<Product>> Create(Product product)
        {
            _context.Products.Add(product);
            await _context.SaveChangesAsync();

            return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
        }

        // PUT action
        [HttpPut("{id}")]
        public async Task<IActionResult> Update(long id, Product product)
        {
            if (id != product.Id)
            {
                return BadRequest();
            }

            _context.Entry(product).State = EntityState.Modified;
            await _context.SaveChangesAsync();

            return NoContent();
        }

        // DELETE action
        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(long id)
        {
            var product = await _context.Products.FindAsync(id);

            if (product == null)
            {
                return NotFound();
            }

            _context.Products.Remove(product);
            await _context.SaveChangesAsync();

            return NoContent();
        }

    }
}

Controllers/ValuesController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace RetailApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public ActionResult<string> Get(int id)
        {
            return "value";
        }

        // POST api/values
        [HttpPost]
        public void Post([FromBody] string value)
        {
        }

        // PUT api/values/5
        [HttpPut("{id}")]
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/values/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}

Data/ProductsContext.cs

using Microsoft.EntityFrameworkCore;
using RetailApi.Models;

namespace RetailApi.Data
{
    public class ProductsContext : DbContext
    {
        public ProductsContext(DbContextOptions<ProductsContext> options)
            : base(options)
        {
        }

        public DbSet<Product> Products { get; set; }
    }
}

Data/SeedData.cs

using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RetailApi.Models;

namespace RetailApi.Data
{
    public static class SeedData
    {
        public static void Initialize(IServiceProvider serviceProvider)
        {
            using (var context = new ProductsContext(serviceProvider
                .GetRequiredService<DbContextOptions<ProductsContext>>()))
            {
                if (!context.Products.Any())
                {
                    context.Products.AddRange(
                        new Product { Name = "Squeaky Bone", Price = 20.99m },
                        new Product { Name = "Knotted Rope", Price = 12.99m }
                    );

                    context.SaveChanges();
                }
            }
        }
    }
}   

Models/Product.cs

using System.ComponentModel.DataAnnotations;

namespace RetailApi.Models
{
    public class Product
    {
        public long Id { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        [Range(minimum: 0.01, maximum: (double)decimal.MaxValue)]
        public decimal Price { get; set; }
    }
}

Program.cs

using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using RetailApi.Data;

namespace RetailApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateWebHostBuilder(args).Build();
            SeedDatabase(host);
            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();

        private static void SeedDatabase(IWebHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;

                try
                {
                    var context = services.GetRequiredService<ProductsContext>();
                    context.Database.EnsureCreated();
                    SeedData.Initialize(services);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "A database seeding error occurred.");
                }
            }
        }
    }
}

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.EntityFrameworkCore;
using RetailApi.Data;

namespace RetailApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ProductsContext>(options =>
                options.UseInMemoryDatabase("Products"));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

ソースの詳細については、公式サイトをご参照ください。

docs.microsoft.com