20211007のC#に関する記事は3件です。

.Net6.0でワーカーサービスを作ると、UserSecretsがうまく働かない

はじめに .Net6.0でワーカーサービスを作ってみた。 デバッグ実行しても、UserSecretsが読み取られない。 これ書いてる時点では直ってないけど、あなたが読んでるときには直ってるかもしれません。 とりあえずアップデートしましょう。 アップデートしても直らない場合は↓ 解決方法 UserSecretsのパッケージを、明示的に読み込むと直る。 NuGetするか、プロジェクトファイルを直接編集してどうぞ。 <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.0-rc.1.21451.13" /> ワーカーサービスのプロジェクト作ると、自動でHostingのパッケージが追加されます。 UserSecretsは、Hostingパッケージの依存関係にあるから、一緒に読み込まれてるはずなのに… .Net5.0では、Hostingのパッケージだけで、うまいことUserSecretsも有効になります。 具体的な症状 プロジェクトファイルにUserSecretsIdを入れてビルドすると、アセンブリにUserSecretsId属性がつきます。 ユーザシークレットさんは、この属性を見て、secrets.jsonのパスを特定しています。 この属性が無い場合、読み込まれません。 現在の.Net6.0では、UserSecretsパッケージを直接参照しないと、この属性がつかないっぽい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Prismで「新しく画面が開かれたとき」と「次画面から戻ってきたとき」を区別して処理する

Prismにおける画面遷移のおさらい 画面遷移時の処理として、PrismのINavigationAwareインターフェイスには以下のメソッドが用意されています。To、Fromが直感と反するかもしれませんが、後ろに来るのがthisだと思えば間違えないでしょう。 OnNavigatedTo(NavigationContext navigationContext) 他の画面からこの画面に遷移したときのイベントです。 OnNavigatedFrom(NavigationContext navigationContext) この画面から他の画面に遷移するときのイベントです。 navigationContextは勝手に設定されます。画面を開くときはnavigationContext.NavigationServiceが必要です。NavigationService.RequestNavigate(string target, NavigationParameters navigationParameters)メソッド(拡張メソッドです)を使用することで別画面を開くことができます。そのため、以下のようにOnNavigatedToのときに必ずnavigationContext.NavigationServiceを保持することになるでしょう。 なお、処理の順番は以下となります。 コンストラクタ OnNavigatedToメソッド 画面のLoadedイベント public class ViewModel : BindableBase, INavigationAware { protected IRegionNavigationService RegionNavigationService { get; private set; } = null!; public void OnNavigatedTo(NavigationContext navigationContext) { RegionNavigationService = navigationContext.NavigationService; } public void OnNavigatedFrom(NavigationContext navigationContext) { } } 画面遷移の区別 さて、このOnNavigatedToですが、ある画面から新しく開かれたときとある画面を開いたあとに戻ってきたときの両方で発火します。そのため、OnNavigatedToに初期化処理を書いてしまうと、「次画面を開いている間はVMを残しておきたい」というようなケースで困ったことになります。 具体的には以下の要件をすべて満たす場合です。 画面Aから画面Bを開くとき、画面Bはコンストラクタから毎回新しく作りたい。そのときにデータを画面Aから画面Bに渡したい 画面Bを閉じて画面Aに戻るとき、画面Bは完全に破棄したいし、画面Aは画面Bに移動する直前の状態をそのまま表示したい 戻ってくるときに画面Aで何らかの処理(データの更新等)はしたい 任意の画面Tから同じ画面Tは開けない(ループ不可) 画面は常に1枚しか開けない(同じ画面を同時に複数枚開けない) 要は「新しく画面が開かれたとき」と「次画面から戻ってきたとき」を区別して処理できればいいのです。そんな実装方法について紹介します。 判定するだけなら簡単 NavigationService.RequestNavigateメソッドの第2引数でパラメータを指定できるので、以下のように毎回フラグを渡してやる方法もあるでしょう。 このやり方だと処理の判別を外部から受け取るデータ(画面B)に依存しています。新しく開くかどうかは自分自身(画面A)側で保持すべきでしょう。その実装方法は後述します。 public class ViewModel : BindableBase, INavigationAware { protected IRegionNavigationService RegionNavigationService { get; private set; } = null!; public void OnNavigatedTo(NavigationContext navigationContext) { RegionNavigationService = navigationContext.NavigationService; var flag = (bool?)navigationContext.Parameters["IsNewOpen"]; if (flag?.Value ?? true) { /* 新しく開いたとき */ } else { /* 戻ってきたとき */ } } /// <summary>この画面を閉じる処理でtrueにします</summary> private bool _isExiting; public void OnNavigatedFrom(NavigationContext navigationContext) { var param = new NavigationParameters(); param.Add("IsNewOpen", !_isExiting); } } ただしこれだけでは「画面Aから画面Bを開くとき、画面Bはコンストラクタから毎回新しく作りたい」「画面Aに戻ってきたときはそのまま表示したい」を満たせません。 破棄するのか保持するのかは別の方法で管理する必要があります。 破棄の判定 画面を保持するというのは、つまり画面のインスタンスを破棄せず使いまわしておけばよいということです。それを判定するメソッドとして、INavigationAwareにはIsNavigationTarget(NavigationContext navigationContext)が用意されています。 また、IRegionMemberLifetimeインターフェイスを実装すると、画面がいなくなるたびに破棄するかどうか(=表示のたびにインスタンスを再生成するか否か)を制御できます。 この2つを実装することで、「戻ってきたときは過去の画面」「新しく開くときは再生成」を実現することが可能です。 2つの違いについてはわかりやすくまとめられた記事がありますのでこちらも参照してください。 public class ViewModel : BindableBase, INavigationAware, IRegionMemberLifetime { /// <summary>この画面を閉じる処理でtrueにします</summary> private bool _isExiting; public bool IsNavigationTarget(NavigationContext navigationContext) => !_isExiting; public bool KeepAlive => !_isExiting; } この実装により、画面を閉じる際に_isExiting = true;とすることで画面が破棄されるようになります。破棄されているので次に開くときはまたコンストラクタから再生成されます。 自分自身で新しく開かれたか戻ってきたかを判定する版 OnNavigatedToメソッドは毎回実行されるので、要は最初の1回めだけ処理されるように実装をすればよいでしょう。全体のコードを以下に示します。 public class ViewModel : BindableBase, INavigationAware, IRegionMemberLifetime { #region INavigationAwareの実装 protected IRegionNavigationService RegionNavigationService { get; private set; } = null!; /// <summary> /// 新しく開かれたかどうかを取得します。 /// 初回の<see cref="OnNewOpened(NavigationContext)"/>の完了後以降はfalseになります。 /// </summary> protected bool IsNewOpend => _isNewOpend; private bool _isNewOpend = true; /// <summary> /// 他の画面からこの画面に遷移したときのイベントです。 /// </summary> /// <param name="navigationContext">現在のコンテキスト</param> /// <remarks>処理の順番はコンストラクタ→<see cref="OnNavigatedTo(NavigationContext)"/>→<see cref="Loaded"/>です。</remarks> public void OnNavigatedTo(NavigationContext navigationContext) { RegionNavigationService = navigationContext.NavigationService; if (IsNewOpend) OnNewOpened(navigationContext); else OnReOpened(navigationContext); _isNewOpend = false; } /// <summary> /// 他の画面から新しくこの画面を開いたときの処理を行います。 /// </summary> /// <param name="navigationContext">現在のコンテキスト</param> /// <remarks>処理の順番はコンストラクタ→<see cref="OnNewOpened(NavigationContext)"/>→<see cref="Loaded"/>です。</remarks> protected virtual void OnNewOpened(NavigationContext navigationContext) { } /// <summary> /// 他の画面からこの画面に戻ってきたときの処理を行います。 /// </summary> /// <param name="navigationContext">現在のコンテキスト</param> /// <remarks>処理の順番は<see cref="OnReOpened(NavigationContext)"/>→<see cref="Loaded"/>です。 /// コンストラクタは実行されません。</remarks> protected virtual void OnReOpened(NavigationContext navigationContext) { } /// <summary> /// 画面のインスタンスを使いまわすかどうかを取得します。既定値はfalseです。 /// </summary> /// <param name="navigationContext">現在のコンテキスト</param> /// <returns></returns> /// <remarks> /// <para>true:インスタンスを使いまわす(画面遷移してもコンストラクタが呼ばれない)</para> /// <para>false:インスタンスを使いまわさない(画面遷移するとコンストラクタが呼ばれる)</para> /// ※コンストラクタが呼ばれない場合でも、Loadedイベントは起きる /// </remarks> public virtual bool IsNavigationTarget(NavigationContext navigationContext) => !_isExiting; /// <summary> /// この画面から他の画面に遷移するときのイベントです。 /// </summary> /// <param name="navigationContext">現在のコンテキスト</param> public void OnNavigatedFrom(NavigationContext navigationContext) { OnMovingContent(navigationContext); } /// <summary> /// この画面から他の画面に遷移するときの処理を行います。 /// この処理は前画面に戻るときと次画面に進むときの両方で実行されます。 /// </summary> /// <param name="navigationContext">現在のコンテキスト</param> protected virtual void OnMovingContent(NavigationContext navigationContext) { } #endregion #region IRegionMemberLifetimeの実装 /// <inheritdoc/> /// <remarks> /// falseの場合、非アクティブになったこの画面を破棄します。 /// </remarks> public bool KeepAlive => !_isExiting; /// <summary> /// この画面を閉じているときtrue /// </summary> private bool _isExiting; #endregion } 「新しく画面が開かれたとき」はOnNewOpenedメソッドが、「戻ってきたとき」はReOpendedメソッドが実行されます。データを前画面から受け取ったり次画面から引き継いだりするときはこれらメソッド内で処理を行います1。 このような処理はほぼ全画面で共通の処理となりますので、ViewModelの共通の親クラスとして実装し、具体的な画面のコンテンツ側で継承してOnNewOpenedメソッドやOnReOpenedメソッドを必要に応じて実装することをおすすめします。 ただし、最初に条件として上げましたが画面Aから画面Aを開くのようなことをしたり、別ウィンドウで複数開くというケースでは適用できません。あくまでも紙芝居のように前画面を隠しながら1枚ずつ遷移するケースでしか今回のコードは動作しませんので注意してください。 OnNavigatedToはコンストラクタの後に実行されるため、受領データをOnNewOpenedで初めてプロパティやメンバ変数にセットする場合、その変数はコンストラクタ終了時点ではnullであることに注意してください。これを忘れるとNullReferenceExceptionの原因となったり、null参照の未設定警告が出ます。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gtk3アプリ パーシャルクラス自動生成ツール

パーシャルクラス自動生成ツール Release 既存のクラスからパーシャルクラスを生成す るツールを作りました。 使い方 Riderの外部ツールに登録する 引数を設定する 引数はReadMeを参照ください ファイル名に使いたい文字をコピーする エクスプローラーで生成したい元クラスを選択する 元のクラスの中身 外部ツール 実行する Riderのエクスプローラーの右クリックから実行します 生成されたパーシャルクラス パーシャルクラスが同じ階層に生成されます。 生成されたパーシャルクラスの中身 続く
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む