20210226のC#に関する記事は8件です。

Prism コードサンプル学習:07-Modules

Prism コードサンプル学習:07-Modules

はじめに

以下の記事の続きです。
https://qiita.com/mngreen/items/41d3bd3159be3b279a5f

07-Modules

本サンプルではModuleの読み込みを、以下の5パターンの方法で行うやり方です。

App.configを用いた読み込み

  • App.configで読み込まれるようにどこで設定されているか

    • App.xaml.csでCreateModuleCatalogをオーバーライドして
     return new ConfigurationModuleCatalog();
    

    としているが、この内部の呼び先をたどっていくとConfigurationStore.Desktopに到達する。App.configが参照されるのは、ここでConfigurationManagerを利用しているから。

プロジェクトを参照し、IModuleCatalogを用いて読み込み

  • 呼び出されているメソッドはここ
    • このメソッドを呼び出すと型情報からModuleCatalogを生成して追加してくれる。最終的に呼び出されるのはここのはず。

Moduleが格納されているフォルダを指定して読み込み

  • フォルダパスを指定すると、それ以下のModuleをかき集めてくれる。実際に読み込み処理が発生するのはここ
  • ビルド時にModuleフォルダに格納するように設定の変更が必要

ModuleInfoのインスタンスを生成してIModuleCatalogに追加

ModuleInfoをXamlで定義し、そのXamlからModuleCatalogを生成

  • Uriでxamlを指定し、dllを読み込む。処理はここ

    • 以下のように読み込み対象のモジュールを指定する。
    <m:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:m="clr-namespace:Prism.Modularity;assembly=Prism.Wpf">
    
    <m:ModuleInfo ModuleName="ModuleAModule" 
                  ModuleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    
    </m:ModuleCatalog>
    

個人的所感

  • プロジェクト参照が必要な方法は以下。
    • プロジェクトを参照し、IModuleCatalogを用いて読み込み
    • ModuleInfoのインスタンスを生成してIModuleCatalogに追加
  • プロジェクト参照がなくても動的に読み込める方法は以下。
    • App.configを用いた読み込み
    • Moduleが格納されているフォルダを指定して読み込み
    • ModuleInfoをXamlで定義し、そのXamlからModuleCatalogを生成

プロジェクト参照をなくせて、変更が少なく済みそうなModuleが格納されているフォルダを指定して読み込みが良さそうに見える。
他の動的に読み込める方法はバージョンや名前空間が変わるごとにメンテが必要となり大変そう。

おわりに

今回はModuleの読み込む方法について確認しました。
個人的には動的に読み込める恩恵を受けられる方法が良いかと思いますが、Prismが使われているオープンソースでの主流についても把握したほうがよいと思いました。
次回、08-ViewModelLocatorについて見ていこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NETでDockerを利用したローカル開発時にもAWS Profileを使ってシークレットを安全に引き渡す

はじめに

 ローカル開発時に.NETのプログラムからAWSの各サービスを利用するには、認証情報としてAWSのプロファイル情報を利用するか、アクセスキーとシークレットキーを設定する必要があります。プログラムの設定ファイルなどにアクセスキーやシークレットキーを記載すると、誤ってソース管理リポジトリに公開してしまう危険があるため、可能であればAWSプロファイルを利用してAWSに接続することが推奨されます。
 この記事では、Dockerを利用したローカル開発時に、AWS Profileを利用してシークレットを安全に取り扱う方法について解説します。

プロファイルを利用した.NET CoreでのAWSサービスの利用

 .NET CoreでAWSの各サービスを利用する場合は下記のドキュメントに記載されている通り、設定ファイル(appsettings.json)に利用するAWSのプロファイル情報を定義し、Startup時にAddDefaultAWSOptions拡張メソッドで前述の設定内容を有効にすることが推奨されています。
https://docs.aws.amazon.com/ja_jp/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html

具体的には、下記のようになります。

appsettings.json
{
  "AWS": {
    "Profile": "DevProfile",
    "Region": "ap-northeast-1"
  }
}
Startup.cs(AWSサービスのDI設定)
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
        services.AddAWSService<IAmazonSecretsManager>();

        /// ... 略 ...
    }
    /// ... 略 ...
}
サービスの利用
public class HomeController : ControllerBase
{
    private readonly IAmazonSecretsManager _amazonSecretsManager;
    public HomeController (IAmazonSecretsManager amazonSecretsManager)
    {
        _amazonSecretsManager = amazonSecretsManager;
    }
    public async Task<string> Get()
    {
        var secret = await _amazonSecretsManager.GetSecretValueAsync(new GetSecretValueRequest
            {
                SecretId = "MySecret"
            });
        return secret.SecretString;
    }
}

 Visual StudioでIISにホストしたり、dotnet runでWebサイトをセルフホストした場合はこれで問題ありませんが、Dockerにホストした場合はDocker内に設定ファイル(appsettings.jsonで指定した)AWSプロファイルが存在しないため、下記のような例外が発生します。

AWSプロファイルからAWSの接続情報が取得できないために発生する例外
Amazon.Runtime.AmazonServiceException: Unable to get IAM security credentials from EC2 Instance Metadata Service.
   at Amazon.Runtime.DefaultInstanceProfileAWSCredentials.FetchCredentials()
   at Amazon.Runtime.DefaultInstanceProfileAWSCredentials.GetCredentials()
   at Amazon.Runtime.DefaultInstanceProfileAWSCredentials.GetCredentialsAsync()
   at Amazon.Runtime.Internal.CredentialsRetriever.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.ErrorCallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.MetricsHandler.InvokeAsync[T](IExecutionContext executionContext)

Docker利用のAWSプロファイルの利用

 アクセスキーとシークレットキーを設定してもよいのですが、せっかく機密情報を記載しなくてもよい仕組みがあるのでプロファイルを利用したいですよね。AWSプロファイルは、各ユーザーのホームディレクトリ直下の.awsディレクトリに保存されています。このディレクトリ配下の設定情報はどのOSでも共通になっているので、このフォルダーをボリュームマウントしてあげればよさそうです。

Visual Studioを利用している場合は、コンテナーオーケストレーターのサポートから
image.png
Docker Composeを追加して、
image.png
docker-compose.override.ymlにホームディレクトリ/.awsディレクトリを各コンテナのホームディレクトリにマウントする設定を追加してあげましょう。AWSプロファイル名は各開発者で異なる可能性があるので、AWSプロファイル名もこちらに書いておくと良いかもしれませんね。

docker-compose.override.yml
version: '3.4'

services:
  app:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:443;http://+:80
      - AWS__Profile=DevProfile # 追加
    ports:
      - "80"
      - "443"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
      - ${USERPROFILE}/.aws:/root/.aws/:ro # 追加

プロファイルからアクセスキーとシークレットキーが取得できていますね。
image.png

まとめ

  • AWSに接続する場合はできるだけAWSプロファイルを利用する。
  • ローカルでDockerコンテナの中からAWSに接続する場合はホストの.awsディレクトリをマウントしてAWSプロファイルを利用する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

埋め込んだファイルをStreamとして取り出す

はじめに

.NETで一時的なHTTPサーバーを立ち上げて、index.htmlだけを返す必要がありました。
単一のexeで完結させたいので、下記2点を実現したいと考えています。
- index.htmlをexeに埋め込む
- 埋め込んだリソースをStreamとして取り出す

index.htmlをexeに埋め込む

index.htmlのプロパティを開き、ビルドアクションを埋め込みリソースとして設定します。
image.png

埋め込んだリソースをStreamとして取り出す

GetManifestResourceStreamを使うことで、Streamとして埋め込みリソースを取り出せます。
GetManifestResourceStreamの引数には、プロジェクト名.[フォルダ階層.]ファイル名を指定します。

Program.cs
var context = await http.GetContextAsync();

context.Response.ContentType = "text/html";

var assembly = Assembly.GetExecutingAssembly();
using (var file = assembly.GetManifestResourceStream("WindowsFormsApp1.index.html"))
{
    file.CopyTo(context.Response.OutputStream);
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

移動する方向にオブジェクトを傾けさせたい【Unity】

タイトルの通り。
UFOとかドローン、ゴーストみたいな浮遊してる敵とかに使えるかも?
Transform.InverseTransformDirectionという存在を初めて知った。

Simple_Tilt.cs
using UnityEngine;
public class Simple_Tilt : MonoBehaviour
{
    [SerializeField] private float xMultiple;
    private Vector3 latestPos;

    private Vector3 tiltVector;
    private Vector3 localDiff;

    void Update()
    {
        //ワールドでの移動量
        Vector3 diff = transform.position - latestPos;
        latestPos = transform.position;
        //ローカル用に変換する
        localDiff = transform.InverseTransformDirection(diff.normalized);

        tiltVector = new Vector3(
        localDiff.z * xMultiple,
        transform.eulerAngles.y,
        localDiff.x * -xMultiple);

        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(tiltVector), 0.1f);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Packageのインポートで同一のファイル名のシーンやスクリプトがあると上書きされるので注意

Packageをインポートしたらデータが上書きされてびっくり

タイトルの通りなので注意したい。

なんとなく別プロジェクトのUIとかスクリプトを使い回そうとしてインポートしたら起きました。

一旦上書きされてしまうと、バックアップ以外に戻す方法がないっぽい。

テストしてみると、とりあえずsceneとscriptは上書きされてしまいました。

一応警告は出るが、、

パッと情報がなかったので、何かの間違いかもと思ってテストしてみた。

同じファイル名のシーンとスクリプトとがあるプロジェクトを二つ用意

スクリーンショット 2021-02-26 0.50.37.png

スクリプトはこんな感じのやつの表示テキスト部分だけが違を変えたものに。

Hello.cs
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("上書きされた");
    }

エクスポート側

スクリーンショット 2021-02-26 0.59.21.png

インポート側
スクリーンショット 2021-02-26 1.01.45.png

あ、ちっさく警告でてる!。
ここで気づけてチェックを外せばOKだけど、
このままimportを押してしまうと上書きされてしまう。

スクリーンショット 2021-02-26 1.14.19.png

もうちょっとババーンと警告出して欲しいな。

ちなみに昔は警告も出なかったそうな。コワイ。
https://answers.unity.com/questions/1006498/accidentally-overwritten-scripts-with-imported-pac.html
https://answers.unity.com/questions/594502/help-scene-file-overwritten-after-import-package.html
https://www.reddit.com/r/Unity3D/comments/adlhzg/does_unity_just_overwrite_your_scene_if_you/

ちなみにプロジェクトウィンドウの中に同一ファイル名のファイルをコピーしても上書きされない。
スクリーンショット 2021-02-26 1.43.10.png

Finderなどでコピペすると上書き、というか新しいファイルに置き換えられるらしい。
https://amagamina.jp/overwriter/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Packageのインポートは同一のファイル名のシーンやスクリプトを上書きするっぽいので注意

Packageをインポートしたらデータが上書きされてびっくり

タイトルの通りなので注意したい。

なんとなく別プロジェクトのUIとかスクリプトを使い回そうとしてインポートしたら起きました。
GameManagerとかOpenURLとか一部使い回してるけど中身が違うスクリプトが上書きしてしまって焦った。

とりあえずsceneとscriptは丸々上書きされてしまいました。

一旦上書きされてしまうと、バックアップ以外に戻す方法がないっぽい。
今回はDropBoxから復元しました。

ファイル操作のミスとかでも間違って上書きしちゃうことはあるけど、
気をつけたい。

一応警告は出るが、、

パッと情報がなかったので、何かの間違いかもと思ってテストしてみた。

同じファイル名のシーンとスクリプトとがあるプロジェクトを二つ用意

スクリーンショット 2021-02-26 0.50.37.png

スクリプトはこんな感じのやつの表示テキスト部分だけを変えたものに。

Hello.cs
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("上書きされた");
    }

エクスポート側

スクリーンショット 2021-02-26 0.59.21.png

インポート側
スクリーンショット 2021-02-26 1.01.45.png

あ、ちっさく警告でてる!。
ここで気づけてチェックを外せばOKだけど、
このままimportを押してしまうと上書きされてしまう。

スクリーンショット 2021-02-26 1.14.19.png

もうちょっとババーンと警告出して欲しいな。

ちなみに昔は警告も出なかったそうな。コワイ。
https://answers.unity.com/questions/1006498/accidentally-overwritten-scripts-with-imported-pac.html
https://answers.unity.com/questions/594502/help-scene-file-overwritten-after-import-package.html
https://www.reddit.com/r/Unity3D/comments/adlhzg/does_unity_just_overwrite_your_scene_if_you/

その他メモ

あとプロジェクトウィンドウの中に同一ファイル名のファイルをコピーしても上書きされない。
別のファイルとして召喚される。
スクリーンショット 2021-02-26 1.43.10.png

Finder上からの操作であれば上書きできる。
https://amagamina.jp/overwriter/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Packageのインポートで同一ファイル名のシーンやスクリプトが上書きされる

Packageをインポートしたらデータが上書きされてびっくり

タイトルの通りなので注意したい。

なんとなく別プロジェクトのUIとかスクリプトを使い回そうとしてインポートしたら起きました。
GameManagerとかOpenURLとか一部使い回してるけど中身が違うスクリプトが上書きしてしまって焦った。

とりあえずsceneとscriptは丸々上書きされてしまいました。

一旦上書きされてしまうと、バックアップ以外に戻す方法がないっぽい。
今回はDropBoxから復元しました。

ファイル操作のミスとかでも間違って上書きしちゃうことはあるけど、
気をつけたい。

Unityのバージョンは2019.4.1f

一応警告は出るが、、

パッと情報がなかったので、何かの間違いかもと思ってテストしてみた。

同じファイル名のシーンとスクリプトとがあるプロジェクトを二つ用意

スクリーンショット 2021-02-26 0.50.37.png

スクリプトはこんな感じのやつの表示テキスト部分だけを変えたものに。

Hello.cs
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("上書きされた");
    }

エクスポート側

スクリーンショット 2021-02-26 0.59.21.png

インポート側
スクリーンショット 2021-02-26 1.01.45.png

あ、ちっさく警告でてる!。
ここで気づけてチェックを外せばOKだけど、
このままimportを押してしまうと上書きされてしまう。

スクリーンショット 2021-02-26 1.14.19.png

もうちょっとババーンと警告出して欲しいな。

ちなみに昔は警告も出なかったそうな。コワイ。
https://answers.unity.com/questions/1006498/accidentally-overwritten-scripts-with-imported-pac.html
https://answers.unity.com/questions/594502/help-scene-file-overwritten-after-import-package.html
https://www.reddit.com/r/Unity3D/comments/adlhzg/does_unity_just_overwrite_your_scene_if_you/

その他メモ

あとプロジェクトウィンドウの中に同一ファイル名のファイルをコピーしても上書きされない。
別のファイルとして召喚される。
スクリーンショット 2021-02-26 1.43.10.png

Finder上からの操作であれば上書きできる。
https://amagamina.jp/overwriter/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[C#]コンストラクタを呼ばずにインスタンスを作る

以下のようなクラスがあるとき、インスタンスの作成にとても時間がかかってしまいます。

public class Hoge
{
    private int v;
    public int Value => v;

    public Hoge()
    {
        // Application.isPlayingはメインスレッドで呼ばなければ例外が発生する
        Debug.Log($"Application.isPlaying:{Application.isPlaying}");
        // コンストラクタで重い処理をしている
        v = HeavyFunc();
    }

    private int HeavyFunc()
    {
        // 重い初期化処理
        Thread.Sleep(5000);
        // 計算結果
        return 42;
    }
}

インスタンスを作成するときにメインスレッドで行ってしまうとUIが止まってしまうのでできれば別スレッドで作成したいところです。
しかし、コンストラクタ内部でApplication.isPlayingにアクセスしてしまっているため通常はメインスレッドでしか作成することができません。

Task.Run(() =>
{
    try
    {
        var h = new Hoge();
        Debug.Log(h.Value);
    }
    catch (Exception e)
    {
        // UnityException: get_isPlaying can only be called from the main thread.
        // Constructors and field initializers will be executed from the loading thread when loading a scene.
        // Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
        Debug.LogException(e);
    }
});

このクラスを書いたのが自分であれば修正すればいいですがソースコードを修正できない場合もあります。
そのような場合、最終手段として
System.Runtime.Serialization.FormatterServices.GetUninitializedObject
を使うことでコンストラクタを呼ばずにインスタンスを作成することができます。
インスタンスさえ作成できれば後はリフレクションで何とかなります。

// privateなフィールド、メソッドにアクセスするためにリフレクションを使用する
var vInfo = typeof(Hoge).GetField("v", BindingFlags.NonPublic | BindingFlags.Instance);
var heavyFuncInfo = typeof(Hoge).GetMethod("HeavyFunc", BindingFlags.NonPublic | BindingFlags.Instance);
_ = Task.Run(() =>
{
    try
    {
        // コンストラクタを呼ばずにインスタンスを作成
        var hoge = FormatterServices.GetUninitializedObject(typeof(Hoge)) as Hoge;
        // HeavyFuncを実行する
        var result = heavyFuncInfo.Invoke(hoge, null);
        // フィールドに値をセット
        vInfo.SetValue(hoge, result);
        // 正しくインスタンスが作成されている
        Debug.Log(hoge.Value);
    }
    catch (Exception e)
    {
        Debug.LogException(e);
    }
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む