はじめに
ASP.NET Coreは、Microsoftが提供する最新のWebアプリケーション開発フレームワークです。クロスプラットフォーム対応、高いパフォーマンス、そして豊富な機能を備えており、モダンなWeb開発に最適なツールとして注目を集めています。本記事では、ASP.NET Coreの基礎から実践的な開発手法まで、現役エンジニアの視点で詳しく解説していきます。
この記事を読んだらわかること:
- ASP.NET Coreの基本概念と従来の.NET Frameworkとの違い
 - MVCパターンを用いた実践的なWebアプリケーション開発の方法
 - パフォーマンス最適化とメモリ使用量の改善テクニック
 - セキュリティ対策の実装方法と具体的な防御手段
 - Entity Framework Coreを使用したデータベース連携の実践手法
 - DockerとCI/CDを活用したモダンなデプロイメント手法
 
ASP.NET Coreとは何か – モダンWeb開発の新標準
ASP.NET Coreは、Microsoftが開発した最新のWebアプリケーションフレームワークです。従来の.NET Frameworkを基盤とするASP.NETとは異なり、オープンソースでクロスプラットフォーム対応の新しいフレームワークとして設計されています。
従来の.NET Frameworkとの決定的な違い
ASP.NET CoreとASP.NET Frameworkの主要な違いは以下の点にあります:
| 特徴 | ASP.NET Core | ASP.NET Framework | 
|---|---|---|
| プラットフォーム | クロスプラットフォーム(Windows, Linux, macOS) | Windowsのみ | 
| 依存性 | 完全モジュール化(必要な機能のみ導入可能) | モノリシック(全機能が統合) | 
| パフォーマンス | 高速(軽量設計) | 比較的重い | 
| 開発方式 | オープンソース | プロプライエタリ | 
| デプロイ | 柔軟(自己完結型展開可能) | IIS依存 | 
特筆すべき点として、ASP.NET Coreでは必要な機能のみを選択してインストールできる「Pay-for-what-you-use」モデルを採用しています。これにより、アプリケーションの起動時間が短縮され、メモリ使用量も最適化されます。
クロスプラットフォーム対応による新たな可能性
ASP.NET Coreの最大の特徴は、真のクロスプラットフォーム対応です。以下のような開発シナリオが可能になりました:
- 開発環境の自由な選択
- Visual Studio Code(Windows/Mac/Linux)
 - Visual Studio(Windows/Mac)
 - JetBrains Rider(全プラットフォーム)
 
 - デプロイ先の柔軟な選択
- Linux系サーバー
 - Windows Server
 - Dockerコンテナ
 - クラウドプラットフォーム(Azure, AWS, GCP)
 
 
実際の開発では、以下のようなプロジェクト作成コマンドで、どのプラットフォームでも同じように開発を始めることができます:
# 新規Webアプリケーションプロジェクトの作成 dotnet new webapp -n MyFirstAspNetCoreApp # APIプロジェクトの作成 dotnet new webapi -n MyFirstWebApi
高速で軽量な実行環境の実現方法
ASP.NET Coreは、以下の技術的特徴により高いパフォーマンスを実現しています:
Kestrelサーバー
- ネイティブで高性能なWebサーバー
 - 非同期I/O処理による高スループット
 - メモリ効率の高い実装
 
最適化されたミドルウェアパイプライン
public void Configure(IApplicationBuilder app)
{
    // 必要最小限のミドルウェアのみを使用
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
JITコンパイル最適化
- プラットフォーム特有の最適化
 - Tiered Compilationによる実行時最適化
 - ReadyToRun形式による起動時間の短縮
 
効率的なメモリ管理
// Spanを使用した効率的なメモリ操作の例
public void ProcessLargeData(ReadOnlySpan<byte> data)
{
    // スタックアロケーションを最小限に抑える
    Span<byte> buffer = stackalloc byte[1024];
    // データ処理...
}
これらの特徴により、ASP.NET Coreは以下のようなパフォーマンス指標を達成しています:
リクエスト処理速度: 従来の約2倍
メモリ使用量: 最大50%削減
アプリケーション起動時間: 約30%短縮
ASP.NET Coreは、これらの革新的な特徴により、モダンなWeb開発の新標準として急速に普及しています。特に、マイクロサービスアーキテクチャやコンテナ化された環境での開発において、その真価を発揮します。
ASP.NET Coreで作る最新のWebアプリケーション
ASP.NET Coreを使用した最新のWebアプリケーション開発では、モダンな開発手法とベストプラクティスを活用することで、保守性が高く拡張しやすいアプリケーションを構築できます。
新規プロジェクトのセットアップと基本設定
新規プロジェクトの作成から基本設定まで、以下の手順で効率的に開発環境を整えることができます:
プロジェクトの作成
# MVCプロジェクトの作成 dotnet new mvc -n ModernWebApp cd ModernWebApp # 必要なパッケージの追加 dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
アプリケーション設定の構成
var builder = WebApplication.CreateBuilder(args);
// サービスの追加
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// アプリケーションの構築
var app = builder.Build();
// ミドルウェアの設定
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Model-View-Controllerパターンの実装方法
MVCパターンの効果的な実装例を見ていきましょう:
モデルの定義
public class Product
{
    public int Id { get; set; }
    [Required]
    [StringLength(100)]
    public string Name { get; set; }
    [Range(0, 1000000)]
    public decimal Price { get; set; }
    public string Description { get; set; }
    [DataType(DataType.Date)]
    public DateTime CreatedDate { get; set; }
}
コントローラーの実装
public class ProductController : Controller
{
    private readonly IProductService _productService;
    private readonly ILogger<ProductController> _logger;
    public ProductController(IProductService productService, ILogger<ProductController> logger)
    {
        _productService = productService;
        _logger = logger;
    }
    public async Task<IActionResult> Index()
    {
        try
        {
            var products = await _productService.GetAllProductsAsync();
            return View(products);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "製品一覧の取得中にエラーが発生しました");
            return StatusCode(500);
        }
    }
}
ビューの作成
@model IEnumerable<ModernWebApp.Models.Product>
<div class="container">
    <h2>製品一覧</h2>
    <div class="row">
        @foreach (var product in Model)
        {
            <div class="col-md-4 mb-4">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">@product.Name</h5>
                        <p class="card-text">@product.Description</p>
                        <p class="card-text">
                            <strong>価格:</strong> @product.Price.ToString("C")
                        </p>
                    </div>
                </div>
            </div>
        }
    </div>
</div>
依存性注入を活用した疎結合な設計手法
ASP.NET Coreの依存性注入(DI)を活用した実装例:
サービスインターフェースの定義
public interface IProductService
{
    Task<IEnumerable<Product>> GetAllProductsAsync();
    Task<Product> GetProductByIdAsync(int id);
    Task<Product> CreateProductAsync(Product product);
}
サービス実装
public class ProductService : IProductService
{
    private readonly ApplicationDbContext _context;
    private readonly IMapper _mapper;
    public ProductService(ApplicationDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }
    public async Task<IEnumerable<Product>> GetAllProductsAsync()
    {
        return await _context.Products
            .AsNoTracking()
            .OrderByDescending(p => p.CreatedDate)
            .ToListAsync();
    }
    // 他のメソッド実装...
}
DIコンテナへの登録
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddAutoMapper(typeof(Program).Assembly);
builder.Services.AddScoped<IEmailService, EmailService>();
// カスタム設定の注入
builder.Services.Configure<EmailSettings>(
    builder.Configuration.GetSection("EmailSettings"));
このような実装により、以下のメリットが得られます:
テスト容易性の向上
コンポーネントの再利用性
保守性の向上
実装の柔軟性
ASP.NET Coreのこれらの機能を活用することで、スケーラブルで保守性の高いWebアプリケーションを効率的に開発することができます。
パフォーマンスを最大化するASP.NET Coreの機能
ASP.NET Coreは、高いパフォーマンスを実現するための多様な機能を提供しています。適切な実装方法を理解し、これらの機能を効果的に活用することで、アプリケーションの応答性と処理能力を大幅に向上させることができます。
非同期処理による応答性の向上テクニック
非同期プログラミングを効果的に実装することで、アプリケーションのスケーラビリティと応答性を向上させることができます:
非同期コントローラーアクションの実装
public class ProductController : Controller
{
    private readonly IProductService _productService;
    public ProductController(IProductService productService)
    {
        _productService = productService;
    }
    // 非同期アクションメソッド
    public async Task<IActionResult> Index()
    {
        // ConfigureAwait(false)を使用して不要な同期コンテキストの切り替えを防ぐ
        var products = await _productService.GetProductsAsync().ConfigureAwait(false);
        return View(products);
    }
    // 並列処理を活用した非同期処理
    public async Task<IActionResult> Dashboard()
    {
        var tasks = new[]
        {
            _productService.GetTotalSalesAsync(),
            _productService.GetTopProductsAsync(),
            _productService.GetRecentOrdersAsync()
        };
        // 複数の非同期処理を並列実行
        await Task.WhenAll(tasks);
        var viewModel = new DashboardViewModel
        {
            TotalSales = tasks[0].Result,
            TopProducts = tasks[1].Result,
            RecentOrders = tasks[2].Result
        };
        return View(viewModel);
    }
}
非同期ストリーム処理の活用
public async IAsyncEnumerable<Product> GetProductStreamAsync()
{
    await using var context = new ProductContext();
    var products = context.Products.AsAsyncEnumerable();
    await foreach (var product in products)
    {
        // 大量データを少しずつ処理
        yield return product;
    }
}
キャッシュ機能の効果的な活用方法
ASP.NET Coreは、複数のレベルでキャッシュを実装できます:
メモリキャッシュの実装
public class CacheProductService : IProductService
{
    private readonly IMemoryCache _cache;
    private readonly IProductRepository _repository;
    public async Task<Product> GetProductByIdAsync(int id)
    {
        string cacheKey = $"product_{id}";
        return await _cache.GetOrCreateAsync(cacheKey, async entry =>
        {
            entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
            entry.SetSlidingExpiration(TimeSpan.FromMinutes(2));
            return await _repository.GetProductByIdAsync(id);
        });
    }
}
分散キャッシュの設定(Redis使用)
public void ConfigureServices(IServiceCollection services)
{
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = Configuration.GetConnectionString("Redis");
        options.InstanceName = "ProductCache_";
    });
}
レスポンスキャッシュの実装
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]
public async Task<IActionResult> ProductList()
{
    var products = await _productService.GetProductsAsync();
    return View(products);
}
メモリ使用量の最適化戦略
メモリ使用量を最適化するための主要な戦略:
オブジェクトプーリングの実装
public class ApiController : Controller
{
    private readonly ObjectPool<StringBuilder> _stringBuilderPool;
    public ApiController(ObjectPoolProvider poolProvider)
    {
        _stringBuilderPool = poolProvider.CreateStringBuilderPool();
    }
    public IActionResult ProcessData(string data)
    {
        var sb = _stringBuilderPool.Get();
        try
        {
            // StringBuilder を使用した処理
            sb.Append("処理結果: ").Append(data);
            return Ok(sb.ToString());
        }
        finally
        {
            _stringBuilderPool.Return(sb);
        }
    }
}
効率的なメモリ管理
public class DataProcessor
{
    // 大きな配列の効率的な処理
    public async Task ProcessLargeDataAsync(Stream stream)
    {
        const int bufferSize = 4096;
        using var memoryStream = new MemoryStream();
        Memory<byte> buffer = new byte[bufferSize];
        int bytesRead;
        while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
        {
            await memoryStream.WriteAsync(buffer[..bytesRead]);
        }
    }
}
これらの最適化技術を適切に組み合わせることで、ASP.NET Coreアプリケーションの性能を最大限に引き出すことができます。
アプリケーションの重要な指標を監視
メモリ使用量
レスポンス時間
スループット
GCの頻度
ボトルネックの特定と対策
プロファイリングツールの活用
パフォーマンステストの実施
継続的なモニタリング
セキュリティ対策の実装ガイド
ASP.NET Coreには、堅牢なセキュリティ機能が組み込まれています。適切な実装により、アプリケーションを様々な脅威から保護することができます。
認証・認可の実装ベストプラクティス
Identity機能の実装
public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>(options =>
    {
        // パスワードポリシーの設定
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireUppercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.MinLength = 12;
        // ロックアウトポリシー
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
        options.Lockout.MaxFailedAccessAttempts = 5;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();
}
JWT認証の実装
public class AuthenticationService : IAuthenticationService
{
    private readonly IConfiguration _configuration;
    private readonly UserManager<ApplicationUser> _userManager;
    public async Task<string> GenerateJwtToken(ApplicationUser user)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.Role, "User")
        };
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
            _configuration["JWT:SecretKey"]));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var token = new JwtSecurityToken(
            issuer: _configuration["JWT:Issuer"],
            audience: _configuration["JWT:Audience"],
            claims: claims,
            expires: DateTime.Now.AddHours(1),
            signingCredentials: credentials
        );
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}
クロスサイトスクリプティング対策の実践
XSS防御の実装
public void ConfigureServices(IServiceCollection services)
{
    services.AddAntiforgery(options =>
    {
        options.Cookie.Name = "X-CSRF-TOKEN";
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    });
    services.Configure<HtmlEncoderOptions>(options =>
    {
        options.HtmlAttributeEncode = true;
    });
}
CSPヘッダーの設定
public void Configure(IApplicationBuilder app)
{
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Add(
            "Content-Security-Policy",
            "default-src 'self'; " +
            "script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
            "style-src 'self' 'unsafe-inline';"
        );
        await next();
    });
}
セキュアな設定とデータ保護の方法
データ保護APIの実装
public class SecureDataService
{
    private readonly IDataProtector _protector;
    public SecureDataService(IDataProtectionProvider provider)
    {
        _protector = provider.CreateProtector("SecureData.v1");
    }
    public string ProtectData(string data)
    {
        return _protector.Protect(data);
    }
    public string UnprotectData(string protectedData)
    {
        return _protector.Unprotect(protectedData);
    }
}
セキュアな設定管理
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // セキュアな設定の管理
        services.AddDataProtection()
            .PersistKeysToFileSystem(new DirectoryInfo(@"path\to\keys"))
            .SetDefaultKeyLifetime(TimeSpan.FromDays(90));
        // HTTPSリダイレクションの強制
        services.AddHttpsRedirection(options =>
        {
            options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
            options.HttpsPort = 443;
        });
        // セッション設定
        services.AddSession(options =>
        {
            options.Cookie.Name = ".MyApp.Session";
            options.Cookie.HttpOnly = true;
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            options.IdleTimeout = TimeSpan.FromMinutes(20);
        });
    }
}
これらのセキュリティ対策を適切に実装することで、アプリケーションを様々な脅威から保護し、ユーザーデータの安全性を確保することができます。
- 基本的なセキュリティ対策
- HTTPSの強制
 - セキュアクッキーの使用
 - 適切な認証・認可の実装
 - XSS対策の実装
 - CSRF対策の実装
 
 - データ保護
- 機密データの暗号化
 - セキュアなストレージの使用
 - キーの定期的なローテーション
 - バックアップの暗号化
 
 - 監視とログ記録
- セキュリティイベントのログ記録
 - 異常検知の実装
 - 監査ログの保護
 
 
実践的なデータベース連携
ASP.NET Coreでのデータベース連携は、主にEntity Framework Core(EF Core)を使用して実装します。効率的なデータアクセスと保守性の高いコードを実現するための実践的な手法を見ていきましょう。
Entity Frameworkを使用したCRUD操作の実装
DbContextの設定
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // リレーションシップの設定
        modelBuilder.Entity<Product>()
            .HasOne(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId);
        // インデックスの設定
        modelBuilder.Entity<Product>()
            .HasIndex(p => p.Name);
        // 複合キーの設定
        modelBuilder.Entity<OrderItem>()
            .HasKey(oi => new { oi.OrderId, oi.ProductId });
    }
}
リポジトリパターンの実装
public class ProductRepository : IProductRepository
{
    private readonly ApplicationDbContext _context;
    public ProductRepository(ApplicationDbContext context)
    {
        _context = context;
    }
    // 非同期でのCRUD操作
    public async Task<Product> CreateAsync(Product product)
    {
        await _context.Products.AddAsync(product);
        await _context.SaveChangesAsync();
        return product;
    }
    public async Task<Product> UpdateAsync(Product product)
    {
        _context.Entry(product).State = EntityState.Modified;
        await _context.SaveChangesAsync();
        return product;
    }
    public async Task<bool> DeleteAsync(int id)
    {
        var product = await _context.Products.FindAsync(id);
        if (product == null) return false;
        _context.Products.Remove(product);
        await _context.SaveChangesAsync();
        return true;
    }
    public async Task<IEnumerable<Product>> GetAllAsync()
    {
        return await _context.Products
            .Include(p => p.Category)
            .AsNoTracking()
            .ToListAsync();
    }
}
マイグレーションによるデータベース管理
マイグレーションの作成と適用
# マイグレーションの作成 dotnet ef migrations add InitialCreate # マイグレーションの適用 dotnet ef database update
マイグレーションコードの例
public partial class AddProductInventory : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<int>(
            name: "StockQuantity",
            table: "Products",
            type: "int",
            nullable: false,
            defaultValue: 0);
        migrationBuilder.CreateIndex(
            name: "IX_Products_StockQuantity",
            table: "Products",
            column: "StockQuantity");
    }
    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "StockQuantity",
            table: "Products");
    }
}
パフォーマンスを考慮したクエリ最適化
効率的なクエリの実装
public class OptimizedProductRepository : IProductRepository
{
    private readonly ApplicationDbContext _context;
    // クエリの最適化例
    public async Task<IEnumerable<ProductSummary>> GetProductSummariesAsync()
    {
        return await _context.Products
            .AsNoTracking()  // 読み取り専用クエリの最適化
            .Select(p => new ProductSummary  // 必要なプロパティのみ選択
            {
                Id = p.Id,
                Name = p.Name,
                Price = p.Price,
                CategoryName = p.Category.Name
            })
            .ToListAsync();
    }
    // ページング処理の実装
    public async Task<(List<Product> Items, int TotalCount)> GetPagedAsync(
        int pageNumber, 
        int pageSize)
    {
        var query = _context.Products
            .Include(p => p.Category)
            .AsNoTracking();
        var totalCount = await query.CountAsync();
        var items = await query
            .OrderBy(p => p.Name)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();
        return (items, totalCount);
    }
    // 複雑な検索条件の実装
    public async Task<IEnumerable<Product>> SearchAsync(ProductSearchCriteria criteria)
    {
        var query = _context.Products.AsQueryable();
        if (!string.IsNullOrEmpty(criteria.Name))
            query = query.Where(p => p.Name.Contains(criteria.Name));
        if (criteria.MinPrice.HasValue)
            query = query.Where(p => p.Price >= criteria.MinPrice.Value);
        if (criteria.MaxPrice.HasValue)
            query = query.Where(p => p.Price <= criteria.MaxPrice.Value);
        return await query
            .AsNoTracking()
            .ToListAsync();
    }
}
インデックスの適切な設定
必要なデータのみの取得
N+1問題の回避
クエリの結果キャッシング
トランザクション管理
public async Task<bool> ProcessOrderAsync(Order order)
{
    using var transaction = await _context.Database.BeginTransactionAsync();
    try
    {
        // 注文の登録
        await _context.Orders.AddAsync(order);
        // 在庫の更新
        foreach (var item in order.Items)
        {
            var product = await _context.Products.FindAsync(item.ProductId);
            product.StockQuantity -= item.Quantity;
        }
        await _context.SaveChangesAsync();
        await transaction.CommitAsync();
        return true;
    }
    catch
    {
        await transaction.RollbackAsync();
        throw;
    }
}
これらの実装方法を適切に組み合わせることで、効率的で保守性の高いデータベース連携を実現できます。
デプロイメントとCI/CDパイプライン
ASP.NET Coreアプリケーションの効率的なデプロイメントと継続的な開発・デリバリーを実現するための実践的な方法を解説します。
Dockerコンテナを使用した展開方法
Dockerfileの作成
# ビルドステージ FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src # プロジェクトファイルのコピーと依存関係の復元 COPY ["MyApp.csproj", "./"] RUN dotnet restore # ソースコードのコピーとビルド COPY . . RUN dotnet build -c Release -o /app/build RUN dotnet publish -c Release -o /app/publish # 実行ステージ FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app COPY --from=build /app/publish . EXPOSE 80 ENTRYPOINT ["dotnet", "MyApp.dll"]
docker-compose.ymlの設定
version: '3.8'
services:
  webapp:
    build: 
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__DefaultConnection=Server=db;Database=MyApp;User=sa;Password=YourPassword;
    depends_on:
      - db
  db:
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=YourPassword
    ports:
      - "1433:1433"
    volumes:
      - dbdata:/var/opt/mssql
volumes:
  dbdata:
クラウドプラットフォームへのデプロイ手順
Azure App Serviceへのデプロイ設定
# azure-pipeline.yml
trigger:
  - main
variables:
  azureSubscription: 'Azure Production'
  webAppName: 'myapp-production'
  vmImageName: 'ubuntu-latest'
stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: DotNetCoreCLI@2
      inputs:
        command: 'build'
        projects: '**/*.csproj'
        arguments: '--configuration Release'
    - task: DotNetCoreCLI@2
      inputs:
        command: 'publish'
        publishWebProjects: true
        arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
    - task: PublishBuildArtifacts@1
      inputs:
        pathtoPublish: '$(Build.ArtifactStagingDirectory)'
        artifactName: 'webapp'
環境別の設定管理
{
  "Production": {
    "ConnectionStrings": {
      "DefaultConnection": "Server=prod-db;Database=MyApp;..."
    },
    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Warning"
      }
    },
    "ApplicationInsights": {
      "ConnectionString": "your-connection-string"
    }
  }
}
自動テストと継続的デプロイの構築
自動テストの実装
public class ProductControllerTests
{
    private readonly IProductService _mockProductService;
    private readonly ProductController _controller;
    public ProductControllerTests()
    {
        _mockProductService = Substitute.For<IProductService>();
        _controller = new ProductController(_mockProductService);
    }
    [Fact]
    public async Task Index_ReturnsViewResult_WithListOfProducts()
    {
        // Arrange
        var expectedProducts = new List<Product>
        {
            new Product { Id = 1, Name = "Test Product" }
        };
        _mockProductService.GetProductsAsync()
            .Returns(expectedProducts);
        // Act
        var result = await _controller.Index();
        // Assert
        var viewResult = Assert.IsType<ViewResult>(result);
        var model = Assert.IsAssignableFrom<IEnumerable<Product>>(
            viewResult.Model);
        Assert.Equal(expectedProducts.Count, model.Count());
    }
}
CI/CDパイプラインの構築(GitHub Actions)
name: .NET Core CI/CD
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '7.0.x'
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --configuration Release --no-restore
    - name: Test
      run: dotnet test --no-restore --verbosity normal
    - name: Publish
      run: dotnet publish -c Release -o publish
    - name: Deploy to Azure Web Apps
      uses: azure/webapps-deploy@v2
      with:
        app-name: 'myapp-production'
        publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
        package: ./publish
環境別の設定ファイル
シークレット管理
環境変数の活用
モニタリングとログの実装
Application Insightsの詳細設定
public void ConfigureServices(IServiceCollection services)
{
    // Application Insightsの高度な設定
    services.AddApplicationInsightsTelemetry(options =>
    {
        options.EnableAdaptiveSampling = true;
        options.EnableQuickPulseMetricStream = true;
        options.EnableDebugLogger = false;
        options.EnableHeartbeat = true;
    });
    // カスタムテレメトリの設定
    services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
}
public class CustomTelemetryInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        telemetry.Context.Cloud.RoleName = "MyWebApp";
        telemetry.Context.Component.Version = "1.0.0";
        if (telemetry is RequestTelemetry request)
        {
            request.Properties["CustomProperty"] = "CustomValue";
        }
    }
}
構造化ログの実装
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _logger;
    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            var startTime = DateTime.UtcNow;
            // リクエスト情報のログ
            _logger.LogInformation(
                "Request {Method} {Path} started at {StartTime}",
                context.Request.Method,
                context.Request.Path,
                startTime);
            await _next(context);
            // レスポンス情報のログ
            var duration = DateTime.UtcNow - startTime;
            _logger.LogInformation(
                "Request completed with status {StatusCode} in {Duration}ms",
                context.Response.StatusCode,
                duration.TotalMilliseconds);
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "Request failed with error: {ErrorMessage}",
                ex.Message);
            throw;
        }
    }
}
カスタムメトリクスの実装
public class MetricsService
{
    private readonly TelemetryClient _telemetryClient;
    private readonly ILogger<MetricsService> _logger;
    public MetricsService(TelemetryClient telemetryClient, ILogger<MetricsService> logger)
    {
        _telemetryClient = telemetryClient;
        _logger = logger;
    }
    public void TrackDatabaseOperation(string operation, TimeSpan duration, bool success)
    {
        _telemetryClient.TrackMetric(new MetricTelemetry
        {
            Name = $"Database.{operation}.Duration",
            Value = duration.TotalMilliseconds
        });
        _telemetryClient.TrackEvent($"Database.{operation}", 
            new Dictionary<string, string>
            {
                ["Success"] = success.ToString(),
                ["Duration"] = duration.TotalMilliseconds.ToString()
            });
    }
}
これらの実装により、以下のような利点が得られます:
リアルタイムのパフォーマンス監視
異常検知の自動化
詳細なログ分析が可能
カスタムメトリクスによる業務指標の追跡
ロールバック戦略の実装
ブルー/グリーンデプロイメントの設定(Azure Pipelines)
stages:
- stage: Deploy
  jobs:
  - deployment: Deploy
    environment: production
    strategy:
      blueGreen:
        # プレプロダクションスロットへのデプロイ
        - task: AzureWebApp@1
          inputs:
            azureSubscription: '$(azureSubscription)'
            appName: '$(webAppName)'
            slotName: 'staging'
            package: '$(Pipeline.Workspace)/drop/**/*.zip'
        # テストの実行
        - task: PowerShell@2
          inputs:
            targetType: 'inline'
            script: |
              $statusCode = (Invoke-WebRequest -Uri "https://$(webAppName)-staging.azurewebsites.net/health").StatusCode
              if ($statusCode -ne 200) { throw "Health check failed" }
        # トラフィックの切り替え
        - task: AzureAppServiceManage@0
          inputs:
            azureSubscription: '$(azureSubscription)'
            action: 'Swap Slot'
            webAppName: '$(webAppName)'
            sourceSlot: 'staging'
            targetSlot: 'production'
カナリアリリースの実装
public class CanaryRoutingMiddleware
{
    private readonly RequestDelegate _next;
    private const double CanaryPercentage = 0.1; // 10%のトラフィック
    public async Task InvokeAsync(HttpContext context)
    {
        var random = new Random();
        var isCanary = random.NextDouble() < CanaryPercentage;
        if (isCanary)
        {
            context.Request.Headers["X-Version"] = "canary";
            // カナリアバージョンへルーティング
            await RouteToCanary(context);
        }
        else
        {
            await _next(context);
        }
    }
}
自動ロールバックの監視設定
public class HealthMonitorService : BackgroundService
{
    private readonly ILogger<HealthMonitorService> _logger;
    private readonly IDeploymentService _deploymentService;
    private int _failureCount = 0;
    private const int FailureThreshold = 3;
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var isHealthy = await CheckApplicationHealth();
            if (!isHealthy)
            {
                _failureCount++;
                _logger.LogWarning("Health check failed. Failure count: {Count}", _failureCount);
                if (_failureCount >= FailureThreshold)
                {
                    _logger.LogError("Health check threshold exceeded. Initiating rollback...");
                    await _deploymentService.RollbackToLastKnownGoodVersion();
                }
            }
            else
            {
                _failureCount = 0;
            }
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
    private async Task<bool> CheckApplicationHealth()
    {
        try
        {
            // アプリケーションの健全性チェック
            // - API エンドポイントの応答確認
            // - データベース接続の確認
            // - 重要な依存サービスの状態確認
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Health check failed");
            return false;
        }
    }
}
これらの実装により、以下のような利点が得られます:
無停止でのバージョン切り替え
段階的なトラフィック移行
自動化された障害検知と復旧
リスクを最小限に抑えたデプロイメント
これらの実装により、効率的で安全なデプロイメントプロセスを確立できます。
最後に
ASP.NET Coreは、モダンなWeb開発における強力なフレームワークとして、高いパフォーマンスとセキュリティ、優れた拡張性を提供します。基本的な概念から実践的な実装手法まで、本記事で解説した内容を活用することで、効率的で堅牢なWebアプリケーション開発が可能となります。継続的な学習と実践を通じて、ASP.NET Coreの真価を発揮させましょう。

