sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

Prism Full App (.NET Core) テンプレートを体験する

f:id:shikaku_sh:20211013180838p:plain:w400

Visual Studio 拡張機能Visual Studio Marketplace から「Prism Template Pack」をインストールすることができます。これに「Prism Full App (.NET Core)」というテンプレートがあり、Prism 製作者が提案するプロジェクトのテンプレートがあるので、構造を理解して参考にしよう、という記事です。

f:id:shikaku_sh:20211013154915p:plain:w500
テストするときはダウンロードしてください

テンプレートの構成について

基本的には以下の記事でよいのだけど、サンプルのリンクが切れていたりするので、そのあたりをフォローした内容になります。

f:id:shikaku_sh:20211013154859p:plain

上図のように、テンプレートは6つのプロジェクトを持ちます。整理すると以下のとおり:

  • WPF_Core_FullSample
    • Core
    • Modules.ModuleName
    • Services.Interfaces
      • Services.Services
    • Tests

テンプレートを1つずつ整理します。

WPF_Core_FullSample プロジェクト

プロジェクトのエントリーポイント。(実際の Window を含むなど)基本になる。

プロジェクト参照は以下:

  • Core
  • Modules.ModuleName
  • Services.Interfaces
    • Services.Services

テストを除いたすべてのプロジェクトが参照されているため、一番上位のプロジェクトであることがわかります。(お互いのプロジェクト A, B は相互参照できないですし、するべきではない)

  • 「A ←→ B」とはできない
  • 「A ← B」or「A → B」となる

Core プロジェクト

Modules に追加されるプロジェクトが参照するような共通クラスを定義します。なので、他のプロジェクト参照がありません。

プロジェクト参照は以下:

  • なし

VM の基底クラスであったり、ContentRegion が定義されています。Core という名前なのもですが、全てのプロジェクトから参照を受けてもよいデータを格納すること、というのが原則になります。

Modules.ModuleName プロジェクト

View, ViewModel を定義します。

プロジェクト参照は以下:

  • Core
  • Services.Interfaces

Services.Interface のみを参照しているので、実装を担うプロジェクト Services.Serives と繋げていないところがポイント。

ModuleName というのは、Modules.作ったモジュール名 ってことなので、このプロジェクトは、削除して作り直したほうがよいです。

Services.Interfaces プロジェクト

Services.Services のモデル(インターフェース)を定義します。なので、他のプロジェクト参照がありません。

プロジェクト参照は以下:

  • なし

Modules に格納されるプロジェクトから利用されるのが基本です。ただし、全てのプロジェクトから参照を受けてもよいデータを格納すること、というのが原則になります。

この考え方は結構わかりやすくて、DB の場合だと以下のように考えるとわかりやすいと思いました。

具体的な実装と切り離すことができているので、DB の変更も容易です。具体的には、エントリーのプロジェクトの修正だけ。(RegisterTypes でインターフェースを解決する実装クラスの変更)VM にこれを仕込むのは、とても慎重な設計ですね。

Prism に関係していない点もポイント。

Services.Services プロジェクト

Services.Interfaces プロジェクトで定義したインターフェースを具体的に実装します。 Services を丁寧に設計した理由がここに詰まる感じだと思います。

プロジェクト参照は以下:

  • ServicesInterfaces

参照がないので、どうやってデータを作るのか、参照を増やすのか、注意が必要。ロジックとビューを完全に切り離す設計になっていると思います。

Prism に関係していない点もポイント。

新しいサービスを追加するテスト

IMessageService のサンプルとして、IMessageServiceAsync を追加してみます。(元サンプルのとおりですが

Services.Interfaces の修正

namespace WPF_Core_FullSample.Services.Interfaces
{
    public interface IMessageService
    {
        string GetMessage();
    }

    public interface IMessageServiceAsync
    {
        ValueTask<string> GetMessageAsync();
    }
}

Services.Services の追加

GetMessageAsync の具体的なコードを以下とします。 JsonSerializer を利用しているので、System.Text.Json をNuGet からダウンロードしておこう。

namespace WPF_Core_FullSample.Services
{
    public class MessageServiceAsync : IMessageServiceAsync
    {
        private readonly HttpClient _HttpClient;

        public MessageServiceAsync(HttpClient httpClient)
        {
            _HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }

        public async ValueTask<string> GetMessageAsync()
        {
            using var jsonStream = await _HttpClient.GetStreamAsync("https://raw.githubusercontent.com/runceel/mockapi/master/message.json");

            // 2秒遅らせる
            await Task.Delay(2000);

            var result = await JsonSerializer.DeserializeAsync<MessageResult>(
                jsonStream,
                new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                });

            return result.Message;
        }

        public class MessageResult
        {
            public string Message { get; set; }
        }
    }
}

ちなみに URL から取得しているデータは json データで次のとおり:

{
  "message": "Hello from GitHub"
}

Modules.ModuleName の修正

ViewAViewModel クラスに MessageAsync プロパティを追加して、コンストラクタの引数をひとつ増やして IMessageServiceAsync を追加します。

ViewAViewModel.cs の修正:

public class ViewAViewModel : RegionViewModelBase
{
    private string _message;
    public string Message
    {
        get { return _message; }
        set { SetProperty(ref _message, value); }
    }

    private string _messageAsync = "Now Loading...";
    public string MessageAsync
    {
        get { return _messageAsync; }
        set { SetProperty(ref _messageAsync, value); }
    }

    public ViewAViewModel(IRegionManager regionManager, IMessageService messageService, IMessageServiceAsync messageServiceAsync) :
        base(regionManager)
    {
        Message = messageService.GetMessage();
        messageServiceAsync.GetMessageAsync().AsTask().ContinueWith(p =>
        {
            MessageAsync = p.Result;
        });
    }

    public override void OnNavigatedTo(NavigationContext navigationContext)
    {
        //do something
    }
}

ViewA.XAML の修正:

<UserControl x:Class="WPF_Core_FullSample.Modules.ModuleName.Views.ViewA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WPF_Core_FullSample.Modules.ModuleName.Views"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True" >
    <StackPanel Background="Gray" Orientation="Vertical">
        <TextBlock Margin="10" Text="{Binding Message}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="24"/>
        <TextBlock Margin="10" Text="{Binding MessageAsync}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="24"/>
    </StackPanel>
</UserControl>

WPF_Core_FullSample プロジェクトの修正

DI パターンの設定です。ここまでやると、DI をすることで疎結合がしっかりしているのがわかります。

App.xaml.cs の修正:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterSingleton<IMessageService, MessageService>();
    containerRegistry.RegisterSingleton<IMessageServiceAsync, MessageServiceAsync>();
}

実際のうごきはサンプルのとおり。

サンプル

GitHub にサンプルを公開しています。

f:id:shikaku_sh:20211013154758g:plain:w500

参考

DI パターンを体験するために IoC Unity を使ってみる

f:id:shikaku_sh:20210823173353p:plain:w300

IoC Unity とは、ゲームエンジンの Unity ではないです。inversion of control (IoC) コンテナの Unity です。

DI コンテナを C# で体験してみるなら、デファクトスタンダートに近い存在である Unity コンテナを触ってみることにします。

以下のコードは、参考リンクのサンプルコードを個人的に試してみたものになります。

テスト1

class Sample2
{
    public interface IMasterManager
    {
        DataSet Read();
    }

    public class CustomerManager : IMasterManager
    {
        public DataSet Read() => new DataSet("Customer");
    }

    public class SupplierManager : IMasterManager
    {
        public DataSet Read() => new DataSet("Supplier");
    }

    public class ProductManager : IMasterManager
    {
        public DataSet Read() => new DataSet("Product");
    }

    public void Test()
    {
        var container = new UnityContainer();

        container.RegisterInstance<IMasterManager>("Customer", new CustomerManager());
        container.RegisterInstance<IMasterManager>("Supplier", new SupplierManager());
        container.RegisterInstance<IMasterManager>(new ProductManager());

        var manager1 = container.Resolve<IMasterManager>("Customer");
        var manager2 = container.Resolve<IMasterManager>("Supplier");
        var manager3 = container.Resolve<IMasterManager>();

        // ERROR ここからは呼び出せない
        // var manager4 = container.Resolve<IVehicle>("Bike");

        Console.WriteLine(manager1.Read().DataSetName);
        Console.WriteLine(manager2.Read().DataSetName);
        Console.WriteLine(manager3.Read().DataSetName);

        // 1, 2 だけ
        var managers = container.ResolveAll<IMasterManager>();

        foreach (var manager in managers)
        {
            Console.WriteLine(manager.Read().DataSetName);
        }
    }
}

出力結果:

Customer
Supplier
Product
Customer
Supplier

Unity コンテナにオブジェクトを格納して、インターフェース経由でオブジェクトを取得します。 キーになっているものはテキストなので、迷うところはないと思います。

スト2

class Sample
{
    public interface IAnimal
    {
        void Cry();
    }

    public class Cat : IAnimal
    {
        public void Cry() => Console.WriteLine("ニャ~");
    }

    public class Dog : IAnimal
    {
        public void Cry() => Console.WriteLine("バウ!");
    }

    public class Person
    {
        [Dependency("Dog")]
        public IAnimal Pet { get; set; }

        public void Call()
        {
            Pet.Cry();
        }
    }

    public void Test()
    {
        UnityContainer container = new UnityContainer();

        container.RegisterInstance<IAnimal>(nameof(Dog), new Dog());
        container.RegisterInstance<IAnimal>(nameof(Cat), new Cat());

        var person = new Person();

        person = container.BuildUp(person);

        person.Call();
    }
}

出力:

バウ!

Dependency 属性で Person は、Dog を取得します。これもあまり迷うところはないけど、サービスロケータ感がちょっと臭う感じですので、利用の際は注意がちょっとだけ必要かも。

とはいえ、文字列で紐づいているだけなので、コンテナ自身と具体的に結びついているわけではないのがポイント。BuildUp メソッドを呼び出したコンテナが構築する流れです。なので、かなりデメリットを脱臭できていると思います。

基本はコンストラクターからインターフェースを受け取る形式から始めてみて、手に負えないときはこのような属性を利用したセッターを検討するとよいと思います。(Martin Fowler の意見を尊重するなら)

テスト3

class Sample
{
    public class AnimalExtension : UnityContainerExtension
    {
        protected override void Initialize()
        {
            Container.RegisterType(typeof(IAnimal), typeof(Dog), "Dog", TypeLifetime.Transient);
            Container.RegisterType(typeof(IAnimal), typeof(Cat), "Cat", TypeLifetime.Transient);
        }
    }

    public void Test()
    {
        var container = new UnityContainer();

        container.AddNewExtension<AnimalExtension>();

        var person = container.Resolve<Person>();

        person.Call();
    }
}

出力結果:

バウ!

拡張を利用したコンテナ作成のサンプルです。TypeLifetime でインスタンスのライフタイムを決めることができる点がおもしろいです。

テスト4

class Sample
{
    public interface IVehicle
    {
        int Run();
    }

    public class Car : IVehicle
    {
        private int _Miles = 0;
        public int Run() => ++_Miles;
    }

    public class Bike : IVehicle
    {
        private int _Miles = 0;
        public int Run() => ++_Miles;
    }

    public class Driver
    {
        private IVehicle _Vehicle;

        [InjectionConstructor]
        public Driver(IVehicle vehicle)
        {
            _Vehicle = vehicle;
        }

        public void Run() => Console.WriteLine($"Run {_Vehicle.GetType().Name} - {_Vehicle.Run()} mile.");
    }

    public class Driver2
    {
        private IVehicle _Vehicle1;
        private IVehicle _Vehicle2;

        [InjectionConstructor]
        public Driver2(IVehicle vehicle1, IVehicle vehicle2)
        {
            _Vehicle1 = vehicle1;
            _Vehicle2 = vehicle2;
        }

        public void Run()
        {
            Console.WriteLine($"Run2 {_Vehicle1.GetType().Name} - {_Vehicle1.Run()} mile.");
            Console.WriteLine($"Run2 {_Vehicle2.GetType().Name} - {_Vehicle2.Run()} mile.");
        }
    }

    public void Test()
    {
        var container = new UnityContainer();

        container.RegisterType<IVehicle, Car>(nameof(Car));
        container.RegisterType<IVehicle, Bike>(nameof(Bike));

        container.RegisterType<Driver>(nameof(Car) + nameof(Driver), new InjectionConstructor(container.Resolve<IVehicle>(nameof(Car))));
        container.RegisterType<Driver>(nameof(Bike) + nameof(Driver), new InjectionConstructor(container.Resolve<IVehicle>(nameof(Bike))));

        container.RegisterType<Driver2>(
            nameof(Driver2), 
            new InjectionConstructor(
                container.Resolve<IVehicle>(nameof(Car)), 
                container.Resolve<IVehicle>(nameof(Bike))
            )
        );

        var car = container.Resolve<IVehicle>(nameof(Car));

        var carDriver = container.Resolve<Driver>(nameof(Bike) + nameof(Driver));
        var driver2 = container.Resolve<Driver2>(nameof(Driver2));

        car.Run();
        carDriver.Run();
        driver2.Run();
        driver2.Run();
    }

}

出力結果:

Run Bike - 1 mile.
Run2 Car - 1 mile.
Run2 Bike - 1 mile.
Run2 Car - 2 mile.
Run2 Bike - 2 mile.

素直にコンストラクターから DI を受け取る形式です。なので、一番の基本形になるのかなと思います。

サンプルコードとしては、一番長くなったので最後にまわしたけど。

サンプル

GitHub にテストコードをあげています。

参考

DI パターンと DI コンテナについて

DI とは「dependency」を「injection」するプログラミングのデザインパターンのこと。日本語訳だと「依存性の注入」となっており、どのように意味を解釈したものかむつかしくなっています。

この部分を個人的に理解を整理した(つもりな)ので、その内容をメモ。

DI

f:id:shikaku_sh:20211011132544j:plain:w200
Martin Fowler

まず、「DI」という用語は Martin Fowler の「Inversion of Control Containers and the Dependency Injection pattern」という記事が元になっているようです。(Martin Fowler は、リファクタリング、XP、アジャイルなんかでも有名ですね)

As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.

ざっくりと雑訳:

結論をいえば、このパターンにはもっと具体的な名前が必要だと思います。Inversion of Control (制御の反転) では、あまりにも一般的な用語なので、人々を混乱させてしまう。なので、IOC を支持するさまざまな人と議論を重ねた結果、Dependency Injection という名前に落ち着きました。

で、DI の意味について WIKI を参照すると:

In software engineering, dependency injection is a technique in which an object receives other objects that it depends on, called dependencies.

DI は、あるオブジェクトが依存している他のオブジェクトを受け取るテクニックのこと、となります。

なので、DI は、デザインパターンと同じで「DI パターン」と書かれる(呼ばれる)こともあります。コーディングのテクニックなので。ただここで注意したいのは、上述の概念を満たしたテクニックはすべて DI パターンと呼ぶことができます。

DI を満たしたコーディング方法として「DI コンテナ」というものがあります。

DI コンテナ

あるオブジェクトは、受け取るオブジェクトが(n個)あると、生成コードも複雑になるので、DI を楽に実現するためにコンテナを用意して、コンテナから必要なオブジェクトを取り出して使おう、ということになります。

static void Main(string[] args)
{
    var container = GetContainer();

    var client = container.Resolve<Client>();
    client.Do1();
}

static IContainer GetContainer()
{
    var builder = new ContainerBuilder();

    builder.RegisterType<Client>();
    builder.Build();

    return builder;
}

やっていること自体は単純なのだけど、単純ゆえに自由度が高くていろいろな使い方がされたりするので、目的を区切るような注意が必要です。(以下、アンチパターンの例)

サービスロケータのアンチパターン

class Client
{
    private Container _Container;
    private ILogger _Logger;
    private IDo1 _Do1;
    private IDo2 _Do2;

    public Client(Container container)
    {
        _Container = container;

        _Logger = _Container["logger"];
        _Do1 = _Container["do1"];
        _Do2 = _Container["do2"];
    }

    public void Write(string text) => _Logger.Write(text);
    public void DoSomething1() => _Do1.Do();
    public void DoSomething2() => _Do2.Do();
}

DI コンテナをコンストラクタに渡して、クラスの中でコンテナ内のオブジェクトを探すパターンを、DI パターンの DI コンテナ を利用した手法の中でも、サービスロケータと呼んでいます。(歴史的な流れから別パターン扱いすることもあります)

サービスロケータは、DI コンテナをクラスの中に組み込んだことで、(DI コンテナ自体と)より蜜月な依存関係を作りこんでしまいます。なので、あまり推奨されない傾向にあります。

言い換えると、これは DI コンテナから依存性のあるオブジェクトを受け取っておらず、自ら DI コンテナに問い合わせて、依存性のある必要なオブジェクトを取得しています。

ただし、これを許容する場合もあります:

class Client
{
    [Dependency("logger")]
    private ILogger _Logger;
    [Dependency("do1")]
    private IDo1 _Do1;
    [Dependency("do2")]
    private IDo2 _Do2;

    public void Write(string text) => _Logger.Write(text);
    public void DoSomething1() => _Do1.Do();
    public void DoSomething2() => _Do2.Do();
}

次のようにリファクタリングすることもあります:

class Client
{
    private ILogger _Logger;
    private IDo1 _Do1;
    private IDo2 _Do2;

    public Client(ILogger logger, IDo1 do1, IDo2 do2)
    {
        _ILogger = logger;
        _Do1 = do1;
        _Do2 = do2;
    }

    public void Write(string text) => _Logger.Write(text);
    public void DoSomething1() => _Do1.Do();
    public void DoSomething2() => _Do2.Do();
}

実際のところは、サービスロケータでも必要なオブジェクトを受け取ることができてしまっているので、両者の違いを区別しづらいところはある。

DI はインターフェースに依存するため、「具体ではなく、抽象に依存する」というSOLID 原則の D(依存性逆転の原則)を満たすことから、テストがやりやすくなるメリットを主に受けることができます。

サービスロケータは、WPF の場合だと VM に DI を利用してテストしやすくするかと言えば微妙なので、そういうところでは Prism なんかはサービスロケータ寄りになってたりもする。(けど、DB からデータ(レコード)を受け取る部分を DI で作るなら、サービスロケータっぽくはしません。ケースバイだと思います)

テストと DI

テストにも少し触れたので補足します。

こういう記事があって、 DI はテストを簡単にするんでエライという内容です。でも、コメントで否定的な意見が結構ついてる。

なお、最初のころから DI を支持する共通の理由に「テストを簡単にする」というものは含まれています。(2004 年の記事です)

A common reason people give for preferring dependency injection is that it makes testing easier.

Inversion of Control Containers and the Dependency Injection pattern - Martin Fowler

なので、DI を説明する際には、テストから話を進めてもいいと思うし、SOLID 原則から話を進めてもよくて、好みの問題だったり、鶏が先か卵が先か (chicken or the egg) の話だと思います。

まとめ

  • DI (DI パターン)
    あるオブジェクトが依存している他のオブジェクトを受け取るテクニックのこと
  • DI コンテナ
    DI を楽に実現するためにコンテナを用意して、コンテナから必要なオブジェクトを取り出して使おう、という考え
  • サービスロケータ
    DI コンテナを利用したコーディングテクニックのひとつで、注意が必要なアンチパターン(あるいは DI とは別のパターンとも言われる。歴史的な順序の問題などで)

なによりも重要なことは、サービスの構成とその使用を確実に分離すること。これは、「インターフェースを実装から分離する」原則と同様の基本的な設計です。

The important issue in all of this is to ensure that the configuration of services is separated from their use. Indeed this is a fundamental design principle that sits with the separation of interfaces from implementation.

Inversion of Control Containers and the Dependency Injection pattern - Martin Fowler

参考

Heroku Postgres と navicat を接続する

f:id:shikaku_sh:20210917173942p:plain

Heroku Postgres と navicat をテスト接続する機会があったので、その内容をメモ。

f:id:shikaku_sh:20210917184141p:plain

珍しく RDBMS でも無料で使えるクラウドサービスです。行数の制限が厳しいのでテスト用だけど用途次第で便利。

Heroku Postgres を有効化

まず、Heroku で Postgres を利用するときは Create new app でプロジェクトを作成して、プロジェクトの Resources > Add-ons で「Heroku Postgres」を追加します。(Free)

f:id:shikaku_sh:20210917173119p:plain:w500 f:id:shikaku_sh:20210917173138p:plain:w500

「Heroku Postgres」を開いて、Settings から「Database Credentials」を確認します。navicat の設定に必要な情報がすべて表示できます。

  • Host
  • Database
  • User
  • Port
  • Password
  • (URI) 上記5つをまとめたもの

f:id:shikaku_sh:20210917173217p:plain:w500

navicat の設定

上記5つの設定(ホスト、データベース、ユーザー、ポート、パスワード)を Navicat の接続設定に入力します。

f:id:shikaku_sh:20210917173235p:plain:w500

古い navicat lite を使っていると、SSL 設定が通らないのでエラーになります。navicat essentials は OK です。

SSL タブで以下を設定:

  • SSL を使用する(有効化)
    • SSL モード:require

f:id:shikaku_sh:20210917173316p:plain:w500

この状態で「接続をテストする」を選択して、正しく動作するかどうかを確認します。

データベースタブの設定から、Heroku の「Database Credentials」で確認したデータベース ID に該当するものを自動オープンにチェックします。

f:id:shikaku_sh:20210917173329p:plain:w500

この設定をしないと、大量の DB スキーマが表示されるため、設定しておくことをオススメします。

f:id:shikaku_sh:20210917173414p:plain:w500

接続できたので、おまけでテーブルを作って動作をチェックします。

テーブルのサンプル

ID を作ったときは、シーケンスで自動的に ID を割り当てしておくと便利。(autoincrement みたいなもの)

f:id:shikaku_sh:20210917173433p:plain

  • nextval('シーケンス名')
  • nextval('sample_id_autoincrement'::regclass)

ちなみに作成したシーケンス。

f:id:shikaku_sh:20210917173529p:plain:w500

Postgre の機能です。MySQL の AUTO_INCREMENT 相当。レコードには単純でユニークな値を持たせてほしい。

参考

TypeScript セットアップ方法のメモ

f:id:shikaku_sh:20210908174035p:plain:w400

TypeScript を使うコーディングをはじめるときに、VScode だとターミナルを使ってプロジェクトの初期設定をすると思います。

この部分は、新しくてよい書き方の更新が本当にはやいので、とりあえず最近書いたものをメモ。

うっかり TSLint を入れないようにするために書きました。

ターミナルで実行するコマンド

# package.json の準備
npm init
# typescript のインストール
npm install --save-dev typescript
# Node.js 用の型宣言
npm install --save-dev @types/node
# tsconfig.json の作成
npx tsc --init
# ESLint パッケージのインストール
npm install --save-dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser

2021 年現在、TypeScript のリンターとして推奨されるのは ESLint です。(2019 年に TSLint は非推奨になりました)

ESLint パッケージのインストールは3つをまとめていて、それぞれ:

  • ESLint パッケージ
  • ESLint の TypeScript 用プラグイン
  • ESLint が TypeScript を理解できるようにパースする役割のパッケージ

バージョンをチェックしてみます。

$ ./node_modules/.bin/eslint --version
v7.32.0

tsconfig.json ファイルの修正

自分の環境にあわせて設定すること。

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Projects */

    /* Language and Environment */
    "target": "es6",                                     /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "lib": ["ES6"],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */

    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */

    /* JavaScript Support */

    /* Emit */
    "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    "outDir": "./dist",                                   /* Specify an output folder for all emitted files. */

    /* Interop Constraints */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */

    /* Completeness */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "include": [
    "./src"
  ]
}

ESLint の設定ファイルを追加

手動で、TypeScript の設定ファイル(ESLint の設定ファイルを読み込むためのもの)を作成します。

touch tsconfig.eslint.json
{
    "extends": "./tsconfig.json",
    "include": [
      "src/**/*.ts",
      ".eslintrc.js"
    ],
    "exclude": [
      "node_modules",
      "dist"
    ]
  }

手動で、ESLint の設定ファイルを作成します。

touch .eslintrc.js
module.exports = {
    root: true,
    parser: '@typescript-eslint/parser',
    parserOptions: {
        sourceType: 'module',
        // cmaVersion: 2019, // Node.js 12 = 2019 (他のバージョンの Node.js を利用しているときは変更)
        tsconfigRootDir: __dirname,
        project: ['./tsconfig.eslint.json']
      },
    env: {
      es6: true,
      node: true,
    },
    plugins: [
      '@typescript-eslint',
    ],
    extends: [
      'eslint:recommended',
      'plugin:@typescript-eslint/eslint-recommended',
      'plugin:@typescript-eslint/recommended',
      'plugin:@typescript-eslint/recommended-requiring-type-checking',
    ],
    rules: {
    },
};

extends の設定はデフォルトでは無効になっている型チェックを有効化しています。型チェックを増やすほど、リントに使う時間が増えるため、トレードオフを考慮する必要があります。

ただ、初心者のうちは型チェックを覚える意味でも拡張しておくほうがよいと私は思います。

実行確認

ESLint の設定ができたら実行できるかチェックします:

npx eslint src/index.ts

src フォルダーに index.ts を追加しています。ファイルの中に何も記述していないため、「何も出力されない」結果が正常です。

すべての .ts ファイルをリンスするときはつぎのとおり:

npx eslint . --ext ts

ESLint を使うにあたってわかるのは、マニュアルにしてもなんにしても英語がある程度わからないと、勘違い(誤訳)が発生したり時間がかかることになる。
なんだかんだいっても、基礎学力は基礎学力であって欠かせないものです。

参考

C# インターネットに接続しているかどうかを調べる方法

f:id:shikaku_sh:20210823173353p:plain:h300

ローカルアプリケーションを作るときに、ネットワーク接続の有無をチェックすることがあります。どういうコードを書くでしょうか。

Windows 10 から Windows 11 への更新要件のひとつに「ネットワーク接続の有無」がありました。Windows 11 の更新チェックツールとして、有名どころ「ReadySunValley」がどんなコードを書いているのかチェックしてみました。

ReadySunValley 0.61.0 のコード

ReadySunValley のコードはこれ:

public bool IsInet()
{
    try
    {
        using (var CheckInternet = new WebClient())
        using (CheckInternet.OpenRead("http://clients3.google.com/generate_204"))
        {
            return true;
        }
    }
    catch
    {
        return false;
    }
}

ReadySunValley 0.52.1 以前のコード

[System.Runtime.InteropServices.DllImport("wininet.dll")]
private extern static bool InternetGetConnectedState(out int Description, int ReservedValue);

public static bool isINet()
{
    return InternetGetConnectedState(out _, 0);
}

補足

コード修正されているもののリリースログを見る限りはバグなどの Issue が挙がってきて、ネットワーク接続の確認方法が修正されたというわけではなさそうです。

ただし「Improved Inet connectivity check」と書いてあるので、改善されたコードということになると思います。

新しいコードでは、WebClient を利用して以下の URL を開いています。この URL が開けたらネットワーク接続は可能ということみたいですが、これはどういった URL なのか:

https でも 204 のステータスコードを確認できます。

調べてみたところだと、「The Chromium Projects - Network Portal Detection」が回答になりそうです。

空のページを HTTP Status 204 で返却することで知られている URL のようです。実際に wget で調べてみると、このとおり。

f:id:shikaku_sh:20210823173306p:plain:w600

この修正は Android のネットワーク接続チェックと似たようなコードに修正したかったのだと思います。Android の場合は以下の URL で同じようなコードを確認しています:

f:id:shikaku_sh:20210823173329p:plain:w600

Android Code Search で「generate_204」を検索すると実際のコードを確認できます。

ちなみに https でも 204 を返しました。

こんなテストコードもありました:

@Test
public void testGetCaptivePortalServerUrl() throws Exception {
    String url = mCm.getCaptivePortalServerUrl();
    assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
}

というわけで、ネットワーク接続の有無のチェックで google 系は URL を叩いてステータスコードを確認してそうです。なので、それに倣ったネットワーク接続の有無のチェックに更新された、と考えることができそうです。

おまけ

(その昔よく利用した)dobon.net では NetworkInterface.GetIsNetworkAvailable メソッドが紹介されています。

Refernece source でコードを確認してみると:

NetworkInterface クラスを取得して、どれかひとつでも if 条件をパスする状態だとネットワーク接続できる、というような感じです。

条件としては:

  1. ネットワークインターフェースが稼働していて (OperationalStatus.Up)
  2. ネットワークインターフェースの種類がトンネル接続でもループバック (!NetworkInterfaceType.Tunnel & Loopback) でもない

また、実際の接続を試してみる、というコードも記載されていますが、yahoo に接続してステータスを確認するような内容になっていて、最適化されたコードではなさそうです。

参考

npm vs npx - その違いは?

npm と npx の違いについて個人的に調べていて参考になった記事を雑に和訳しました。

もとになった記事はこちらです。

npm vs npx

もし Node.js を使ったことがある人なら、必ず npm を使ったことがあるはずです。

npm (node package manager) は、Node.js をインストールするとすぐに利用できる依存性+パッケージのマネージャーです。npm は、開発者がグローバルおよびローカルにパッケージをインストールする方法を提供します。

時々は、あるパッケージを見て、いくつかのコマンドを試してみたいと思うかもしれません。しかし、依存関係のあるパッケージを(あなたの)ローカルフォルダー node_modules にインストールしないと、試してみることはできません。

そこで npx の出番です。

この記事では、npm とnpx の違いを見て、両方を使いこなす方法を紹介します。

まず、npm とはなにか、それを使って何ができるのかを理解しましょう。読み物の助けにビデオを見てみませんか? 

youtube です。動画を見なくても、読み進めることはできます。

npm the package manager

npm にはいくつかの特徴があります。まず何より、オープンソースである Node.js を公開するためのオンラインリポジトリである、ということです。

つぎに、パッケージをインストールして、バージョンや依存関係を管理するための CLI ツールです。npm には何十万もの Node.js ライブラリーやアプリケーションが登録されており、毎日多くのパッケージが追加されています。

npm はそれ自信では、パッケージを実行しません。npm を使ってパッケージを実行するためには、package.json ファイルで実行するパッケージを指定する必要があります。

実行ファイルが npm パッケージ経由でインストールされると、npm はリンクを作成します:

  • ローカルインストールのリンク作成
    ./node_modules/.bin/ ディレクトリー
  • グローバルインストールのリンク作成
    bin/ ディレクトリー

グローバルインストールの例 Linux: /usr/local/bin Windows: %AppData%/npm

npm でパッケージを実行するには、次のようにローカルパスを入力する必要があります:

$ ./node_modules/.bin/your-package

または、package.json ファイルの scripts セクションにつぎのような内容を追加します:

{
  "name": "your-application",
  "version": "1.0.0",
  "scripts": {
    "your-package": "your-package"
  }
}

それから、スクリプト npm run を実行します:

npm run your-package

プレーンな npm を使ってパッケージを実行するためには、(あらかじめに)たくさんの儀式が必要なことがわかります。

ありがたいことに、ここで npx が役にたちます。

npx the package runner

npm のバージョン 5.2.0 以降、npm には mpx がプリバンドルされています。そのため、現在では大方の標準となっています。

npx は npm レジストリーにホストされた依存関係を簡単にインストールして、管理することを目的とした CLI ツールでもあります。

通常は npm でインストールするような Node.js ベースの実行ファイルをとても簡単に実行できるようになりました。

以下のコマンドを実行すると、現在の npm バージョンにこのツールがインストールされているかどうかを確認することができます:

$ which npx

もしも、npx がインストールされていなければ、以下のコマンドでインストールします:

$ npm install -g npx

インストールを確認したら、npx が役立つユースケースをいくつか見てみましょう。

Run a locally installed package easily(ローカルにインストールされたパッケージを簡単に実行する)

ローカルにインストールされたパッケージを実行したいときは、以下のようにタイプするだけです:

$ npx your-package

npx は $PATH またはローカルプロジェクトのバイナリーに <command> または <package> が存在するかどうかを確認して、存在するときは実行します。

Execute packages that are not previously installed(インストールされていないパッケージを実行する)

別の大きなアドバンテージは、前もってインストールしていないパッケージを実行できることです。

$ npx cowsay wow

f:id:shikaku_sh:20210823141746p:plain

時々 CLI ツールで使いたいけど、グローバルにインストールしたくない、といったときにこの使い方は素晴らしいものです。

つまり、ディスクスペースを節約しつつ必要なときだけツールを実行することができます。また、グローバル変数の汚染も少なくなります。

Run code directly from GitHubGitHub から直接コードを実行する)

これはかなりいいです。

npx を使えば、あらゆる GitHub の gist やリポジトリーを実行することができます。ここでは、GitHub gist を実行することにフォーカスしてみましょう。

最も基本的なスクリプトは、メインの JS ファイルと package.json ファイルで構成されます。ファイルをセットアップしたら、あとは、gist へのリンクを実行するだけです。

npx github-url

f:id:shikaku_sh:20210823141806p:plain:w500

ここで」例に使用したコードを見つけることができます。

悪意のあるコードによって起こる申告な問題を避けるため、実行する前にスクリプトを必ず注意深く読んでください。

Test different package versions(異なるパッケージのバージョンをテストする)

npx では、Node.js の異なるバージョンのパッケージやモジュールを簡単にテストすることができます。この素晴らしい機能をテストするために、create-react-app パッケージをローカルにインストールして、次期バージョンをテストしてみます。

このテストでは、出力の最後にいくつかの Dist タグが表示されます。Dist タグは、バージョンの番号のエイリアスを提供するもので、これによって入力がとても簡単になります。

$ npm v create-react-app

f:id:shikaku_sh:20210823141851p:plain:w500

dist-tag を確認するコマンド(canary, latest, next)

npx を使って create-react-app の nextの Dist タグで、サンボックス ディレクトリーの中にアプリを作成してみます。

$ npx create-react-app@next sandbox

npx は next バージョンの create-react-app を一時的にインストールして、アプリをスキャフォールド(足場に)して、その依存関係をインストールします。

インストールすると、次のようにアプリをナビゲートし、起動することができます:

cd sandbox
npm start

React のアプリが自動的にデフォルトブラウザーで開かれます。これで、next バージョンの create-react-app パッケージを実行できました。

まとめ

npx は、バージョン管理や依存関係の問題を回避して、ちょっと試してみたいだけのパッケージをインストールするときに役立ちます。

パッケージ、コマンド、モジュール、GitHub Gist やリポジトリーを実行する簡単な方法を提供します。

まだ npx を使ったことがない方は、今が使い始めるよいときです。

この記事は、「neutrondev.com」に掲載されたものです。

参考