.NETアプリケーションにデフォルト設定を適用する方法

May 23, 2022
執筆者
レビュー担当者

.NETアプリケーションにデフォルト設定を適用する方法

この記事はNiels Swimbergheこちらで公開した記事(英語)を日本語化したものです。

過去に投稿したチュートリアル記事「TwilioとC#および.NETのアプリケーションをより良く構成する方法(英語)」と「SendGridとC#および.NETのアプリケーションをより良く構成する方法(英語)」では、Microsoft.Extensions.ConfigurationAPIを使用してアプリケーションの改善方法をご紹介しました。本稿では、これらのチュートリアルのテクニックを基に、デフォルトのオプションを設定の特定の場所に保存し、別の場所から上書きする方法をご紹介します。 例を挙げて説明します。アプリケーションから送るメールの種類は様々で、ウェルカムメール、パスワードリセットメール、オファーメールなどがあります。メールの種類によって、メールアドレスを使い分けたほうがよい場合もあります。「no-reply@yourdomain.tld」のように、返信不要のアドレスを使うことが多いですが、オファーメールでは受信者からの返信を受け取ることに大きな価値があることもあります。そこで、返信不要のアドレスを使う代わりに、返信を営業担当者に転送し、顧客関係管理(CRM)システムと自動的に統合されるようなメールアドレスを使うことができます。

本稿では、デフォルトの返信用アドレスとして「no-reply@yourdomain.tld」を使用しますが、オファーメールでは、例えば「offers@yourdomain.tld」のように別のメールアドレスを使用します。

必要事項

本稿を最後まで進めるには、以下の項目が必要です。

  • C#および.NETを使用した経験
  • .NETをサポートするOS(Windows・macOS・Linux)
  • Git CLI
  • .NET 6 SDK (これより古い・新しいバージョンでも問題ありません)
  • コードエディタまたはIDE(VS CodeC#プラグインをインストールする、Visual Studio、またはJetBrains Riderをお勧めします)

このチュートリアルのソースコードは、GitHubでご確認いただけます。何か問題が発生した場合は参考にしてください。また、サポートが必要な場合は イシューを送信してください。

はじめに

以下でご紹介する内容は、様々な場面に適用できますが、引き続きEメールの例を使用して進めていきます。

以下のコマンドを実行して、gitを使用してプロジェクトをあなたのマシンにクローンし、プロジェクトディレクトリに移動します。

git clone https://github.com/Swimburger/DotNetDefaultConfiguration.git  --branch start
cd DotNetDefaultConfiguration

クローンしたgitリポジトリには、すでにいくつかのJSONと設定を読み込むコードを格納した.NET 6コンソールアプリケーションが含まれています。

appsettings.jsonファイルは以下のような内容です。

{
  "Emails": {
    "Welcome": {
      "FromEmail": "no-reply@yourdomain.tld",
      "FromName": "Your App Name",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe",
      "Subject": "Welcome to our service!",
      "Body": "Thank you for signing up!"
    },
    "PasswordReset": {
      "FromEmail": "no-reply@yourdomain.tld",
      "FromName": "Your App Name",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe",
      "Subject": "Reset your password",
      "Body": "Click the link below to reset your password."
    },
    "Offer": {
      "FromEmail": "offers@yourdomain.tld",
      "FromName": "Jane",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe",
      "Subject": "Don't miss this offer!",
      "Body": "We have an amazing offer for you!"
    }
  }
}

Program.csファイルは、以下のような内容です。

using Microsoft.Extensions.Configuration;
  
IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();
    
EmailOptions GetEmailOptions(string emailSectionName)
{
    var emailOptions = new EmailOptions();
    
    var emailsSection = config.GetSection("Emails");
    var emailSection = emailsSection.GetSection(emailSectionName);

    emailOptions.FromEmail = emailSection["FromEmail"];
    emailOptions.FromName = emailSection["FromName"];
    emailOptions.ToEmail = emailSection["ToEmail"];
    emailOptions.ToName = emailSection["ToName"];
    emailOptions.Subject = emailSection["Subject"];
    emailOptions.Body = emailSection["Body"];

    return emailOptions;
}

void LogEmailOptions(EmailOptions emailOptions)
{
    Console.WriteLine(@"Logging Email Option:
    FromEmail: {0}
    FromName: {1}
    ToEmail: {2}
    ToName: {3}
    Subject: {4}
    Body: {5}", 
        emailOptions.FromEmail, 
        emailOptions.FromName, 
        emailOptions.ToEmail, 
        emailOptions.ToName, 
        emailOptions.Subject, 
        emailOptions.Body
    );
}

var welcomeEmail = GetEmailOptions("Welcome");
LogEmailOptions(welcomeEmail);

var passwordResetEmail = GetEmailOptions("PasswordReset");
LogEmailOptions(passwordResetEmail);

var offerEmail = GetEmailOptions("Offer");
LogEmailOptions(offerEmail);

public class EmailOptions
{
    public string FromEmail { get; set; }
    public string FromName { get; set; }
    public string ToEmail { get; set; }
    public string ToName { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}

このコードでは、JSONのみをソースとして使用して設定を構築します。そして、 GetEmailOptionsローカル関数を使用して設定を取得し、ウェルカムメール、パスワードリセットメールおよびオファーメールのためのEmailOptionsインスタンスを生成しています。実際のアプリケーションでは、メールの本文を動的に生成し、受信者のメールアドレスも動的に取得するでしょうが、このチュートリアルを進めるにあたっては、この設定で問題ありません。

各メールセクションに対応するEmailOptionsオブジェクトが作成された後、LogEmailOptions関数で設定がログに記録されるので、このコードの結果を見ることができます。

JSONに重複している部分があることにお気づきかもしれません。そこで、デフォルトのセクションを導入します。

.NET CLIを使用して、以下のコマンドでプロジェクトを実行します。

dotnet run
// Output:
//  Logging Email Option:
//      FromEmail: no-reply@yourdomain.tld
//      FromName: Your App Name
//      ToEmail: jon@doe.com
//      ToName: Jon Doe
//      Subject: Welcome to our service!
//      Body: Thank you for signing up!
//  Logging Email Option:
//      FromEmail: no-reply@yourdomain.tld
//      FromName: Your App Name
//      ToEmail: jon@doe.com
//      ToName: Jon Doe
//      Subject: Reset your password
//      Body: Click the link below to reset your password.
//  Logging Email Option:
//      FromEmail: offers@yourdomain.tld
//      FromName: Jane
//      ToEmail: jon@doe.com
//      ToName: Jon Doe
//      Subject: Don't miss this offer!
//      Body: We have an amazing offer for you!

JSONファイルから設定が引き出され、コンソールに出力されていることがわかります。

重複する設定要素のためのデフォルトセクションを追加する

重複する設定要素を削除するために、Defaultsセクションを追加します。appsettings.jsonを開き、Welcomeセクションの前にDefaultsセクションを追加してください。

{
  "Emails": {
    "Defaults": {
      "FromEmail": "no-reply@yourdomain.tld",
      "FromName": "Your App Name",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe"
    },
    "Welcome": {
      "Subject": "Welcome to our service!",
      "Body": "Thank you for signing up!"
    },
    "PasswordReset": {
      "Subject": "Reset your password",
      "Body": "Click the link below to reset your password."
    },
    "Offer": {
      "FromEmail": "offers@yourdomain.tld",
      "FromName": "Jane",
      "Subject": "Don't miss this offer!",
      "Body": "We have an amazing offer for you!"
    }
  }
}

上記のように、重複するFromEmailFromNameToEmail、および ToNameプロパティをWelcomeおよびPasswordResetセクションから削除してください。ただし、Offerセクションでは重複するToEmailToNameプロパティだけを削除して、FormEmailFromNameプロパティはそのままにします。

Program.csファイルのコードを以下のように更新し、設定を特定のメールセクションから取得し、NULL(??)の場合はDefaultsセクションから取得するようにします。

EmailOptions GetEmailOptions(string emailSectionName)
{
    var emailOptions = new EmailOptions();
    
    var emailsSection = config.GetSection("Emails");
    var defaultsSections = emailsSection.GetSection("Defaults");
    var emailSection = emailsSection.GetSection(emailSectionName);

    emailOptions.FromEmail = emailSection["FromEmail"] ?? defaultsSections["FromEmail"];
    emailOptions.FromName = emailSection["FromName"] ?? defaultsSections["FromName"];
    emailOptions.ToEmail = emailSection["ToEmail"] ?? defaultsSections["ToEmail"];
    emailOptions.ToName = emailSection["ToName"] ?? defaultsSections["ToName"];
    emailOptions.Subject = emailSection["Subject"] ?? defaultsSections["Subject"];
    emailOptions.Body = emailSection["Body"] ?? defaultsSections["Body"];

    return emailOptions;
}

??は、Null-coalescing operator(NULL合体演算子)で、左辺の値がNULLの場合、右辺の値を返します。これにより、NULLかどうかを簡潔にチェックし、NULLの場合は代替値を提供できます。

.NET CLIを使用して、プロジェクトを再度実行します。

dotnet run

出力は変わりませんが、特定のセクションで設定を行い、Defaultsセクションから設定をフォールバックできるようになりました。

この方法でもデフォルト設定ができますが、設定セクションを強く型付けされたオブジェクトにバインドすることで、同じ結果をより雄弁に達成できます。

設定バインダーを使ってデフォルト値をバインドおよび上書きする

各設定要素を個別に取得する代わりに、設定バインダーを使用して、設定セクションを強く型付けされているオブジェクトにバインドできます。設定セクションをEmailOptionsのインスタンスにバインドするために使用できるメソッドは2つあります。Get()Bind(object)です。

Getメソッドを使用するには、ジェネリック型の型パラメーターとして希望の型を渡し、 Getメソッドがそのタイプのインスタンスを作成し、設定をオブジェクトにバインドし ます。

var emailsSection = config.GetSection("Emails");
var emailOptions = emailsSection.GetSection(emailSectionName)
    .Get<EmailOptions>();

このメソッドを使用するためには、まずEmailOptionsのインスタンスを作成し、そのインスタンスをParameterに渡します。

var emailOptions = new EmailOptions();
  
var emailsSection = config.GetSection("Emails");
emailsSection.GetSection(emailSectionName)
    .Bind(emailOptions);  

さらに興味深いのは、このBindメソッドを複数回使用できることです。デフォルトとメール固有の設定の両方をバインドしたい場合は、次のようにします。

var emailOptions = new EmailOptions();
  
var emailsSection = config.GetSection("Emails");
emailsSection.GetSection("Defaults").Bind(emailOptions);
emailsSection.GetSection(emailSectionName).Bind(emailOptions);

このテクニックを使用して、ローカル関数を更新してEmailOptionsのインスタンスを作成し、それにDefaultsセクションをバインドし、特定のセクションをバインドしてデフォルトを上書きします。

EmailOptions GetEmailOptions(string emailSectionName)
{
    var emailOptions = new EmailOptions();
    
    var emailsSection = config.GetSection("Emails");
    emailsSection.GetSection("Defaults").Bind(emailOptions);
    emailsSection.GetSection(emailSectionName).Bind(emailOptions);

    return emailOptions;
}

配列は.NETの構成ではあまり一般的ではありませんが、サポートされています。ただし、配列を含む設定セクションを1つのオブジェクトにバインドした場合、配列は互いに上書きされず、1つに統合されます。設定やバインディングで配列を使用する場合は、期待通りに動作することを必ず確認してください。

デフォルトをハードコードする

設定からデフォルト値を引き出すだけでなく、コードに直接デフォルト値を追加することもできます。たとえば、他の人が利用するためのライブラリを開発している場合、一部の設定要素にデフォルト値があると便利ではないでしょうか。

個別の設定要素を引き出すのであれば、このようにデフォルト値を追加することも可能です。

emailOptions.FromEmail = emailSection["FromEmail"]
    ?? defaultsSections["FromEmail"]
    ?? "no-reply@yourdomain.tld";

デフォルト値をハードコードするのは意味がないかもしれませんが、設定要素は必要なので、設定要素がnullであった場合に例外をスローしたいことがあります。その場合、このようにインラインで例外を投げるためにNULL合体演算子を使用することもできます。

emailOptions.FromEmail = emailSection["FromEmail"]
    ?? throw new Exception($"Emails:{emailSectionName}:FromEmail has to be configured");

しかし、設定バインダーを使う場合は、NULL合体演算子を使う必要はなく、代わりに以下のようにオブジェクトのイニシャライザーでデフォルト値を指定できます。

var emailOptions = new EmailOptions
{
    FromEmail = "no-reply@yourdomain.tld",
    ...
};

var emailsSection = config.GetSection("Emails");
emailsSection.GetSection("Defaults").Bind(emailOptions);
emailsSection.GetSection(emailSectionName).Bind(emailOptions);

また、次のようにクラス定義自体にデフォルト値を指定することもできます。

public class EmailOptions
{
    public string FromEmail { get; set; } = "no-reply@yourdomain.tld";
    public string FromName { get; set; }
    public string ToEmail { get; set; }
    public string ToName { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}

次のステップ

これまでに、デフォルトの設定値をコードで提供する方法と、専用の設定セクションである Defaultsを使用する方法を学びました。さらに、チュートリアル記事「TwilioとC#および.NETのアプリケーションをより良く構成する方法(英語)」と「SendGridとC#および.NETのアプリケーションをより良く構成する方法.net(英語)」を参照して、今回ご紹介した内容を応用することをお勧めします。

もし、このチュートリアルが役に立ったなら、ぜひあなたが今取り組んでいるプロジェクトを共有してください。

Niels Swimbergheは、ベルギー系アメリカ人のソフトウェアエンジニアで、Twilioのテクニカルコンテンツクリエイターです。Nielsへのお問い合わせは、Twitter @RealSwimburgerから、そしてNielsの.NET、Azure、Web開発に関する個人ブログは、swimburger.netでご覧いただけます。