うさ☆うさ日記 このページをアンテナに追加 RSSフィード

2006 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 05 | 06 |
2013 | 05 | 08 | 09 | 10 | 11 | 12 |
2014 | 08 |
2015 | 04 | 06 | 08 | 09 | 11 | 12 |
2016 | 01 | 02 | 04 | 05 | 09 |

2016-09-20

[]TSqlParserをテストのモックと組み合わせてみた

出発点

SQL Serverでは、T-SQLのパーサーとジェネレーターがMicrosoft.SqlServer.TransactSql.ScriptDom名前空間のクラスとして、.NETのライブラリで提供されていますが。

例えば、パーサーであるTSqlParser(具象クラスはSQL Serverのバージョンに応じたTSql130Parserとか)を使って、SQLの検証を行うことができます。

そこで、Micro-ORMとSQL記述による開発などで、SQL記述の部分の誤りを、これを利用することで改善できるのではないかと思うものの、以外と使いどころが難しいなどと思ったりもして(´・ω・`)

その課題と、今回やってみたことについて書いてみます。

課題

課題は、検証の対象とするSQLをどう判断するかという部分。

ソース中に記述されたSQL文字列について、Roslynを使って抽出して、TSqlParserでコンパイルタイムで問題を検出、…とかできると良いんですが、これはなかなか厳しそう(´・ω・`)

文字列を検出したとして、それがSQLかそうでないのかを判断する基準がないですし、SQL文字列が動的に組み立てられているケースでは、文字列自体はフラグメントにすぎなかったりするので。

SQL文字列をファイル化(外部化)しているようなケースであれば、その判断は容易にできますが、そもそもファイル化されているのであればエディタの検証とかで済みそうと言う話もあり。


っということで、コンパイルタイムでの問題検出はあきらめ、別途思いついたのが、テスト時にMockと組み合わせる方法です(・ω・)

やってみたこと

なにができるようになったかについて、ライブラリにまとめてNuGetからも落とせるようにしているので、先にその確認から。

まず、テストプロジェクトを作って、NuGetで「Usa.Smart.Mock.Data.SqlServer」とテストコード用に「Dapper」を追加します。

っで、テストとしては以下のようなものを用意します。

using Dapper;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Smart.Mock.Data;
using Smart.Mock.Data.SqlServer;

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        using (var connection = new MockDbConnection())
        {
            connection.SetupCommand(cmd => cmd.SetupResult(1));

            connection.Execute("UPDATE Employee SET Name = @Name WHERE Id = @Id", new { Id = 1234, Name = "うさ☆うさ" });

            var result = connection.ValidateSql();
            Assert.IsTrue(result.Valid, result.ToString());
        }
    }
}

まず、これを実行するとテストは成功します。


次に、connection.Execute()部分をで「WHERE」を「WH ERE」のように書き換えて実行してみると、

Assert.IsTrue に失敗しました。Error [46010] (Line = 1, Column = 34) : 'WH 付近に不適切な構文があります。'

のように、テストに失敗し、SQLの問題箇所も出力されることを確認できましたヽ( ・ω・)ノ

解説

まず、NuGetでUsa.Smart.Mock.Data.SqlServerを追加すると、依存関係で自動的に追加されるMicrosoft.SqlServer.TransactSql.ScriptDomとUsa.Smart.Mock.Dataについて説明しておきます。


Microsoft.SqlServer.TransactSql.ScriptDomは、NuGetで配布されているScriptDomのアセンブリです。


Usa.Smart.Mock.Dataはモック用に自前で用意したライブラリであり、IDbConnection、IDbCommand、IDataReader等のモックを用意することで、本物のDBを使用せずに、あたかもDBの処理が正常終了したかのように結果を上位に返すためのものです。

このモックを使うことで、データアクセス層やいわゆるRepository*1のような部分でインターフェースの分離とモックの作成を行うのではなく、エッジ(DB接続)部分までは本物のコードを使ったテストができるようになります。


そして、今回のメインはValidateSql()を実装するSmart.Mock.Data.SqlServerライブラリになります。

ValidateSql()の実装は、Usa.Smart.Mock.Dataが提供するモッククラス(MockDbConnection等)に対する拡張メソッドとして用意してあり、中身はTSqlParser.Parse()をラッピングしているだけです(・∀・;)


まあ、詳細はこの辺から。

https://github.com/usausa/Smart-Net-Mock-Data/blob/master/Smart.Mock.Data.SqlServer/Data/SqlServer/MockExtensions.cs

結論

っということで、本物のDBを使用せず、モックで済ましてテストする場合についても、併せてSQLの構文チェックができるようになったのでした。

LocalDBとかを使っても、環境面や速度面での面倒さはあるし、ロジックのテストだからといってモックだけで済まそうとすると、SQLの構文誤りに気がつかない、なんてこともあり得るので、少しは役に立つのかな(・ω・)?

*1:余談だけど、ドメインモデルのSource/Sink的な性格を持つものではなく、単にデータアクセスをしている層をなんでもRepositoryって銘々するの、ワイは違和感あるんやで(´・ω・`)

2016-09-17

[]作って理解するDependency Resolver(なぜかDIコンテナと言わないというこだわり)

f:id:machi_pon:20160917184055p:image:right

出発点

今更だけど、Compact Framework*1でもDependency Resolverが使いたくなったので、Ninjectのデッドコピーを組んでみたのですよ(・ω・)

先に.NET 4.6.2/PCL版を作って、そこからCompact Frameworkに移植したわけですが、せっかくなのでその実装について書いてみます。

なお、「Dependency Resolver(意地でもDIと言わない)の何がいいの?」については語るつもりはねーですよ。


とりあえずソースはこちら。

https://github.com/usausa/Smart-Net-Resolver

NuGetでも取得できますのだ。

https://www.nuget.org/packages/Usa.Smart.Resolver

何ができるん?

まだるっこしいんで、サンプルコードを先に。*2

public interface IService
{
}

public class Service : IService
{
}

public class Controller
{
    public IService Service { get; }

    public Controller(IService service)
    {
        Service = service;
    }
}

// Web的なイメージのテストコード
using (var resolver = new StandardResolver())
{
    resolver.Bind<IService>().To<Service>().InSingletonScope();
    resolver.Bind<Controller>().ToSelf();

    var controller1 = resolver.Get<Controller>();
    var controller2 = resolver.Get<Controller>();

    Assert.AreNotSame(controller1, controller2);
    Assert.AreSame(controller1.Service, controller2.Service);
}

っで、コードを見た後で、何ができるのかについて書いておきます。

  • Guice型のDependency Resolverです
  • バインディングの情報を登録しておき、その情報に基づいて依存関係を解決しながらオブジェクトを生成し、その取得が可能です
  • 実装している機能は、本家Ninjectと比較した場合、自分が使いそうなもののみにバッサリ省略しています

Resolver(コンテナ)に登録するのはあくまでバインディングの情報であり、インスタンスを単にDictionaryにプールするだけのなんちゃって実装ではないです。

依存関係の解決も、コンストラクタインジェクションを基本として再帰的にインスタンスの生成を行います(・∀・)

Binding

バインディングシンタックスは下記のようなものをサポートしています(・ω・)

  • Bind<T>()

バインディング情報を作成します。

  • To<TImplementation>()

バインディングを指定された型に関連づけます。

// インターフェース型の要求に対してその実測クラスのインスタンスを返す
resolver.Bind<IService>().To<Service>();
  • ToSelf()

バインディングを自身の型に関連づけます。

// 具象クラスの要求に対してそのインスタンスを返す
resolver.Bind<Controller>().ToSelf();
  • ToMethod(Func<IKernel, T> factory)

バインディングに対して生成するインスタンスのファクトリーメソッドを指定します。

// 型の要求に対して、そのファクトリーをResolverから取得して、そのメソッドでインスタンスを生成する
// この例のケースでは、ISchedulerFactoryに対するバインド情報も登録しておく
resolver.Bind<IScheduler>().ToMethod(_ => _.Get<ISchedulerFactory>().GetScheduler());
  • ToConstant(T value)

バインディングに対して固定のインスタンスを指定します。

// 型の要求に対してその固定のインスタンスを返す
resolver.Bind<Messenger>().ToConstant(Messenger.Default);
  • ToProvider(IProvider provider)

バインディングに対してインスタンスを返すIProviderを指定します。

IProviderの定義は以下のような形で、他のTo系メソッドの中身は、処理に応じたIProviderを指定しているだけのものです。

public interface IProvider
{
    object Create(IKernel kernel, IBinding binding);
}

To系メソッドと使用されるIProvider実装は以下のようになっています。

メソッドIProvider実装概要
ToConstant()ConstantProvider指定された固定値を返す
ToMethod()CallbackProviderFunc<IKernel, T>を評価した結果を返す
To()、ToSelf()StandardProviderコンストラクタ情報から依存性を解決してインスタンスを生成して返す
  • InTransientScope()

バインディングで生成されるインスタンスを揮発性にします。

要は、ライフサイクル管理をなにもしませんのだ(・ω・)

Scope指定が省略された場合のデフォルト動作になります。

// Controllerは毎回異なるインスタンスが生成される
resolver.Bind<Controller>().ToSelf().InTransientScope();
  • InSingletonScope()

バインディングで生成されるインスタンスをシングルトンで管理します。

このスコープで管理されたオブジェクトがIDisposableを実装する場合、ResolverのDispose()時にオブジェクトのDispose()も呼び出されます。

// 要求に対して毎回同じServiceのインスタンスが返される
resolver.Bind<IService>().To<Service>().InSingletonScope()
  • InScope()

ライフサイクル管理の方法(IScope実装)を指定します。

IScopeインターフェースは実際にライフサイクル管理を行うIScopeStorageのファクトリで、InSingletonScope()についてもResolver自体は特殊な扱いをせず、この機構によって実現しています。

IScopeStorageの定義は以下のような形で、InSingletonScope()で使用される実装は、Dictionaryによる単純なオブジェクト管理をしているだけのものになります。

public interface IScopeStorage
{
    void Remember(IBinding binding, object instance);

    object TryGet(IBinding binding);

    void Clear();
}
  • Named(string name)

同じ型に対する複数のバインディングを構築する際に、名前を与えることでそれらを区別できるようにします。

参照する側は、NamedAttribute属性を使うことでインジェクションされるオブジェクト選択できます。

// 名前で識別してインジェクションされる
public class Controller
{
    public Controller([Named("master")] Service service)
    {
...
    }
}

// 名前で識別して登録
resolver.Bind<IService>().ToConstant(new Service("master")).InSingletonScope().Named("master");
resolver.Bind<IService>().ToConstant(new Service("slave")).InSingletonScope().Named("slave");

// masterの方が解決されたものが取得できる
var controller = resolver.Get<Controller>();
  • WithConstructorArgument(string name, object value)、WithConstructorArgument(string name, Func<IKernel, object> factory)

型からインスタンスを生成する場合、通常はコンストラクタの各引数再帰的にResolverで解決されますが、一部の引数の値をバインディング時に指定できます。

// ITimerは再帰的にResolverから取得されるが、timeoutには30が設定される
public class Sceduler
{
    public Sceduler(ITimer timer, int timeout)
    {
    }
}

resolver.Bind<ITimer>().To<Timer>().InSingletonScope();
resolver.Bind<Sceduler>().ToSelf().InSingletonScope().WithConstructorArgument("timeout", 30);

何用かというと、コードでバインディング情報を書くのではなく、設定ファイルから機械的にバインディング情報を作る際に使用するケースを主に想定しています。

  • WithPropertyValue(string name, object value)、WithPropertyValue(string name, Func<IKernel, object> factory)

WithConstructorArgument()のプロパティインジェクションバージョンです。

プロパティインジェクションについてはまた後で(☆ゝω・)b⌒☆

  • WithMetadata(string key, object value)

バインディングに対して任意のメタデータを設定します。

メタデータは何に使うかというと、主に型に対して複数のバインディング情報があるケースで、どのバインディングを使用するかの制約条件で使用します。

制約の話についてはまた後で(・ωー)〜☆

Resolve

Resolverからインスタンスを取得するためのインターフェースとしては、以下の形で1件用と複数件用のものを用意しています。

public interface IResolver
{
    object Resolve(Type type, IConstraint constraint);

    IEnumerable<object> ResolveAll(Type type, IConstraint constraint);
}

また、使いやすくするために、以下の処理を拡張メソッドとして用意しています。

T Get<T>(this IResolver resolver);

T Get<T>(this IResolver resolver, string name);

IEnumerable<T> GetAll<T>(this IResolver resolver);

object Get(this IResolver resolver, Type type);

object Get(this IResolver resolver, Type type, string name);

IEnumerable<object> GetAll(this IResolver resolver, Type type);

っで、IResolverの所に出てきているIConstraintとはなんぞや(・ω・)?、っという話がありますが。

これが、同じ型に対して複数のバインディングがあった際に、使用するバインディング選択するための制約で、絞り込み条件のパラメータクラスにようなものになります。


例えば、Named()により異なる名称のバインディング登録されている場合に、名前に一致する方のバインディングからオブジェクトを生成するためにはstring nameが引数にある拡張メソッドを使えば良いですが、その中身は以下のような感じになっています。

public static T Get<T>(this IResolver resolver, string name)
{
    return (T)resolver.Resolve(typeof(T), new NameConstraint(name));
}

その他

バインディング情報の登録と、インスタンスの取得という基本機能以外の諸々についてです。

複数件をコレクションで取得

例えば、

public class Client
{
    // 登録されているITimer全件を配列で欲しい
    public Client(ITimer[] timers)
    {
...
    }
}

resolver.Bind<ITimer>().ToConstant(new Timer(30)).InSingletonScope().Named("timer1");
resolver.Bind<ITimer>().ToConstant(new Timer(60)).InSingletonScope().Named("timer2");
resolver.Bind<ITimer>().ToConstant(new Timer(120)).InSingletonScope().Named("timer3");

var client = resolver.Get<Client>();

のようなケースへの対応についてですが。


単純にTypeをキーにして処理する場合、Resolverに登録されているのはtypeof(ITimer)の情報なので、typeof(ITimer[])とは異なるわけで(´・ω・`)

例えば、WithConstructorArgument()の機能を使って、

resolver.Bind<Client>().ToSelf().WithConstructorArgument(_ => _.GetAll<ITimer>());

っと書けば、それでも解決できますが、いちいちそう書くのも面倒です(´・ω・`)

っということで、引数の型が配列の場合や、GetGenericTypeDefinition()がIEnumerable<>、ICollection<>、IList<>の場合には特別扱いして、その型の配列を設定するようになっています。*3

プロパティインジェクション

Immutableの観点で、コンストラクタインジェクションだけあれば、まあ、困らないのが通常ですが。

ただ、既存のライブラリとの折り合いが悪いケースとかも想定して、プロパティインジェクションも一応用意しておくのが大人の対応というもの(`・ω・´)

public class Client
{
    [Inject]
    public Service Service { get; set; }
}

上記のように、InjectAttributeがついているプロパティについては、インスタンス生成後に参照を設定してくれる動作になっています。

また、StandardResolverにはInject(object value)メソッドも用意してあり、これはResolverの管理外の既存のオブジェクトに対して、上記と同じ処理を行うメソッドになっています。


インジェクション機能はパイプラインで管理され、標準では、実装としてPropertyInjectorクラスのみが用意してありますが、下記のインターフェースの実装を追加することで、他の方法によるインジェクションにも対応できる構造になっています。

public interface IInjector
{
    void Inject(IKernel kernel, IBinding binding, TypeMetadata metadata, object instance);
}
オブジェクトアクティベーション

インスタンス生成後に、PostConstruct的なものを呼んで欲しいニーズへ対応するための機能です。

これもプロパティインジェクションなどと同様にパイプライン処理になっており、下記のインターフェースの実装を追加することで、生成されたインスタンスに対して各種処理を呼び出すフックポイントを用意してあります。

public interface IActivator
{
    void Activate(object instance);
}

標準では、実装としてInitializeActivatorクラスのみが用意してあり、この処理では対象となるinstanceがIInitializableインターフェースを実装している場合、IInitializable.Initialize()を呼び出す、っという処理になっています。

制約

同じ型に対する複数のバインディング情報に対して、その識別をするための機能です。

これもまだるっこしいので、先にソースから。

例として、バインディング情報のメタデータにキーとなる文字列を設定し、そのキーを制約条件として絞り込むIConstraint実装を作る例は以下のようになります。

// 制約条件
public class HasMetadataConstraint : IConstraint
{
    public string Key { get; }

    public HasMetadataConstraint(string key)
    {
        Key = key;
    }

    public bool Match(IBindingMetadata metadata)
    {
        return metadata.Has(Key);
    }
}

// 2件のバインディングを作成
resolver.Bind<Target>().ToConstant(new Target("default"));
resolver.Bind<Target>().ToConstant(new Target("hoge")).WithMetadata("hoge", null);

// メタデータにhogeがある方を取得
var hoge = resolver.Resolve(typeof(Target), new HasMetadataConstraint("hoge"));

また、NamedAttributeのように、属性で制約条件を指定する場合は以下のようにConstraintAttribute派生を作成して使用します。

// 制約条件の属性
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class HasMetadataAttribute : ConstraintAttribute
{
    public string Key { get; }

    public HasMetadataAttribute(string key)
    {
        Key = key;
    }

    public override IConstraint CreateConstraint()
    {
        return new HasMetadataConstraint(Key);
    }
}

// 使用例
public class HasMetadataConstraintInjectedObject
{
    public Target Target { get; }

    public HasMetadataConstraintInjectedObject([HasMetadata("hoge")] Target target)
    {
        Target = target;
    }
}

var obj = resolver.Get<HasMetadataConstraintInjectedObject>();

Named()メソッドおよびNamed属性による制約も、基本的にはこの仕掛けによって実現しています。

Namedに関する処理で異なるのは、Namedだけは頻出パターンということで、通常のメタデータKey/ValueのDictionaryで管理しているのに対し、メタデータに専用のプロパティを用意している点と、バインディング構築の専用シンタックスを用意している点だけです。


まあ、拡張できる仕組みはあるものの、Namedだけあれば困ることもないと思いますが(´・_・`)

デフォルトバインディング

Resolverを通じて生成するオブジェクトについて、必ずバインディングの情報の登録をする必要があるとすると面倒です。*4

そこで、バインディングが未登録の場合に、IBindingResolver実装を用意することで、自動でバインディングを構築する仕掛けがあります。

標準ではSelfBindingResolverクラスが用意してあり、バインディング情報が見つからない場合には、下記の記述と同じバインディングが構築されるようになっています。

resolver.Bind<Target>().ToSelf();

また、この機能もパイプラインで管理されており、IBindingResolver実装を用意することで、例えば、他のフレームワーク組み込みのセンスの悪いstaticなDependencyServiceみたいなもの*5があったとして、このResolverで解決できない場合にはそっちに解決を移譲、みたいな仕組みを作ることが可能になっています。

カスタマイズ

ここまで、パイプラインや拡張できる仕組みについての記述がありましたが、Resolverの各機能はサブモジュールとして構築されており、それを変更することで動作のカスタマイズが可能になっています。

サブモジュールはResolverが持つマイクロコンテナで管理されており、Configure()メソッドによってその登録情報の変更が可能です。

// カスタムIActivatorの追加
resolver.Configure(c => c.Get<IActivatePipeline>().Activators.Add(new CustomInitializeActivator()));

// カスタムIBindingResolverの追加
resolver.Configure(c => c.Get<IMissingPipeline>().Resolvers.Add(new CustomBindingResolver()));

// アクティベートおよびインジェクションパイプラインの完全無効化
// IInitializableやプロパティインジェクションが無効になる代わりに性能があがる
resolver.Configure(c => c.Remove<IActivatePipeline>());
resolver.Configure(c => c.Remove<IInjectPipeline>());

この考えも本家由来になりますが、バッサリ簡略化して、マイクロコンテナについてはDictionary型のインスタンスベースにコンテナになっています。


ちなみに、最近、こういう風に、コンポーネントのサブモジュールをマイクロコンテナに登録しておき、コンポーネント本体は軽量にしてサブモジュールに処理を委譲、マイクロコンテナに登録されているサブモジュール情報を変更することでコンポーネントの挙動変更が可能、っという作りが好みだったりしています(・∀・)

何ができないの?

このDependency Resolverがサポートしない/するつもりもない機能について、なぜサポートしていないのかと、仮にサポートするとしたらどうなのかとかについて、ちょっと書いてみたり。

AOP( ゚д゚)、ペッ

知らない子ですね( ˘ω˘ )


世の中にはAOPのためのDIみたいな事を言い出す人もいて困りものですが、自分が欲しいのはあくまで「Dependency Resolver」ですので。

そもそも、AOPを何に使うかといえば、トランザクション、ログ、認証あたりをあげる人が多いと思いますが、

  • トランザクション制御はLoanパターンとか、明示的なスコープで制御しろ
  • ログや認証は明示的なフィルタ機構を使ってやれ、フレームワークがその機構を持っていないならそいつが糞なだけ、AOPで無理矢理穴をあけるようなものじゃない*6

っということで論破です(フンスフンス


まあ、もし仮に、本当に仮にAOPしたくなったとしたら、IProxyGeneratorインターフェース作って、StandardProviderの最後でそれをかますとかですが。

その前に、どういうシンタックスで指定したいかの検討からかな(・ω・)?

メソッドインジェクション

これはまあ、単純に自分が使うケースがないから実装してないだけですが(・ω・)

やるとしたら、PropertyInjectorと同様にMethodInjectorを作ってInjectPipelineに使うようにするだけですけど。

ただ、そもそもイラネーやということで、内部で使ってる型のメタデータや、InjectAttributeやNamedAttributeといった属性などについて、メソッドに関する部分を削除してしまっているので、もしMethodInjectorを実装するなら、その辺も修正した方がよくはあります。

循環参照の検知

検知機能を入れる場合には、HashSetなんかを使って型に対する処理の再実行を判断する仕組みを入れることになりますが。

ただ、Resolverを使う側の、オブジェクト間の構成の設計に問題がなければ無駄っちゃ無駄なので、省略しています(゚Д゚;)

リクエストスコープ

Webアプリケーションの場合に、リクエスト毎のライフサイクル管理を行いたいようなケースについてですが。

自分が、あんまりそういう使い方をすることがないので、実装していません(`・ω・´)

IScope/IScopeStorage実装を作る(実装自体は単純なDicitionaryでよく、リクエスト終了時にIScopeStorage.Clear()を呼び出すフィルタ機構を用意すればよい)ことで対応は可能ですが、どのみちWeb固有の話なので、コア機能に入れるようなものではなく、別アセンブリに分ける形になりますが。


っで、リクエストスコープの概念を導入する場合に、1点問題になるのが、より広いスコープのオブジェクトからリクエストスコープのオブジェクトに対する参照がある場合にどうするかという点。

例えば、シングルトンスコープのオブジェクトがリクエストスコープのオブジェクトを参照するような場合にどうするのか?。


まあ、んなものサポートしねーヨ、でもいいと思いますが。

これをどうしてもやりたい場合、ここに来てやっと、否定しまくったAOPというかプロキシーの生成機能が必要になってきます(´・ω・`)

ランタイムでのリクエストスコープのインスタンスを参照するようなプロキシーを作って、本来のオブジェクトの参照の代わりにそのプロキシーをシングルトンスコープのオブジェクトにインジェクションするような形ですね。*7

バインディングの自動登録/設定ファイルからの構築

設定ファイルからの構築については、コアの機能としては作っていないですが、拡張機能として作るつもりもあったりもして。

WithConstructorArgument()の実装を用意したのはそのためというのもあるし(・ω・)


自動登録については、そもそも、Guice型ではそんなもんイラネーヨというのがまずあるし、やるにしても登録ルールの指定とか面倒になるだけだしね〜、という話もありますが。

なによりも、自動登録が必要だと思うと言うことは、どーせ馬鹿みたいに(というか馬鹿だから)、なんにでもインターフェースと実装を用意するような、知能レベルを疑う設計をしているんでしょ?、っという話もあったりして(・∀・;)


ちなみに、本来どこについてIFと実装を分離すべきかについては、よくあるWebシステムみたいなものではなくて、もっといろんな形態のシステムを作った方がわかるようになると思いますです。

実装に依存して責務が明確になっていない設計も悪です、「疎結合は善」という言葉を免罪符にしてなんにでもIFを導入するのも同レベル悪です、思考停止という意味において。

まとめ、組んでみた感想

俺にはこういうのでいいんだよ、こういうので( ˘ω˘ )


今まで、Dependency Resolverの実装について、特に拘りはないとか言っていましたが、実際に自分が必要な機能を考えて組んでみたら、この程度の機能は必要だということになり、単純Dictionary型では事足りないということになりまスタ。

っと言っても、本家Ninjectから比べれば機能は絞りまくっていて、ソースは全体で1kくらいしかないので、お手軽感もあって、自分にはちょうどいい感じです。

サイズと機能面のバランスから、DI的なものの学習用とにも使えるかしら(・ω・)?


ちなみに、性能面についても、割とどうでもいいのですが、一応ふれておきます。

まず、単純なDictionary型よりは当然遅いです。

単純Dictionaryタイプのものに比べれば、そこそこ機能もあり拡張可能にしているので、どうしたってオーバーヘッドは入りますし、マイクロベンチをとればその差は大きく見えたりもします。

そもそも、機能比較の併記がない性能比較に意味はないので、それらとの比較に価値なんてないんですが、自分の用途や、どれくらいの処理オーダーを想定するのかの立脚点がないと、そういうものに惑わされることもありますし(´・ω・`)


一方で、本家から比べた場合には、本家はもっといろいろやっているので、それに比べればこのResolverは骨しか残っていないようなものなので、当然速くはなりますが。

もっとも、自分が本気で高性能なDependency Resolverが必要になったら、オブジェクトをnewするメソッドが並ぶコードを生成する、T4Resolver(イメージとしては以下)とかを用意すると思いますが٩(๑•ㅅ•๑)و

// Auto generated.
private static readonly Lazy<IService> service = new Lazy<IService>(() => new Service());

private static IService GetService()
{
    return service.Value;
}

private static Controller GetController()
{
    return new Controller(GetService());
}

...

っということで、以上が今回の学習内容となります(`・ω・´)ゞ

組んだライブラリの話に見せかけた、ある種の設計に関するdisでした(?)

チッ、反省してま〜す。

*1:ぎょーむ端末の世界ではまだ普通に使われているゾイ(´・ω・`)

*2:ちなみに、この例では解説用にいわゆるサービス層的なものをインターフェースと実装にわけてるけど、ワイはがなんでもIF/実装小僧だとは思わないでね(´・ω・`)

*3:ちなみに、本家ではジェネリッククラスのオープン構築型/クローズ構築型あたりの扱いとかについても、もう少しちゃんとした実装が入っていたりするんですが、こいつは「仕様です」もしくは「そんな使い方はするな」の精神で、そのへんはあまり考慮していません(・ω<)

*4:後述しますが、Guice型の場合自動登録とかするのもチト思想が違うので

*5:おっと、それ以上は言うなよ…、なんのためにPCL版も用意したと思ってるんだ?

*6:まして、パッチ用途に使うみたいなアンチパターンとか…

*7:Springとかは、そんなことやってたと思う

2016-05-10

[]SensorTagをNode.jsで扱う(私家版)

SensorTagをNode.jsで扱うサンプルについて、見かけるサンプルの多くが複数のSensorTagを想定していなかったり、SensorTagのON/OFFをしたさいの再接続に対応していなかったので、対応した版を書いてみました。

IoTごっことかをする場合、アプリは常駐したままでSensorTagのON/OFFをするだろうし、複数のSensorTagを扱えないと実用的ではないと思うので(´・ω・`)


とりあえずソース。

var SensorTag = require('sensortag');

var discovering = false;

// センサー設定
function setupSensor(sensorTag) {
  sensorTag.discoverServicesAndCharacteristics(function() {
    // 温度
    sensorTag.enableIrTemperature(function() {});
    sensorTag.on('irTemperatureChange', function(objectTemperature, ambientTemperature) {
      var data = {};
      data.timestamp = new Date().getTime();
      data.uuid = sensorTag.uuid;
      data.messsage = 'temperature';
      data.objectTemperature = objectTemperature.toFixed(1);
      data.ambientTemperature = ambientTemperature.toFixed(1);

      console.log(JSON.stringify(data));
    });
    sensorTag.notifyIrTemperature(function() {})

    // 湿度
    sensorTag.enableHumidity(function() {});
    sensorTag.on('humidityChange', function(temperature, humidity) {
      var data = {};
      data.timestamp = new Date().getTime();
      data.uuid = sensorTag.uuid;
      data.messsage = 'humidity';
      data.temperature = temperature.toFixed(1);
      data.humidity = humidity.toFixed(1);

      console.log(JSON.stringify(data));
    });
    sensorTag.notifyHumidity(function() {})
  });
};

// センサー探索
function discoverSensor() {
  // discoverの重複実行はしない
  if (discovering) {
    return;
  }
  discovering = true;

  console.log('start discover:');

  // 探索開始
  SensorTag.discover(function(sensorTag) {
    discovering = false;

    console.log('discovered: ' + sensorTag.uuid + ', type = ' + sensorTag.type);

    // 接続開始
    sensorTag.connect(function() {
      console.log('connected: ' + sensorTag.uuid);

      sensorTag.once('disconnect', function() {
        console.log('disconnected: ' + sensorTag.uuid);

        // 探索再開
        discoverSensor();
      });

      setupSensor(sensorTag);

      // 探索再開
      discoverSensor();
    });
  });
};

// 開始
setInterval(function() {
  discoverSensor();
}, 10000);

discoverSensor();

ポイントはSensorTag.discover()の実行タイミングくらい(・ω・)?

通知周りについては、SensorTagを検知したらセンサーを有効にして後はSensorTagに任せるモデルなので、バッファリングや外部への通信については後はconsole.log(JSON.stringify(data))のタイミングで弄るようにしてくださいな。

2016-04-19

[]Xamarin.FormsでUIXAMLで構築する勢には現状XAML Conditional Compilationが必須(・ω・)

Visual StudioXAMLを書く際に、Bindingの補完の為にデザインタイム用のDataContextを設定しますよね、こんな風に(・ω・)

f:id:machi_pon:20160419175630p:image

しかし、Xamarin.FormsではこのXAMLコンパイルして実行すると、XamlParseException 「No Property of name Ignorable found」という例外が発生してしまいます(´・ω・`)

かといって、補完を捨てるわけにはいかないのでどうするか?

XAML Conditional Compilationを使って解決しましょう。


手順としては、まずはNuGetでXAMLを使っているXamarin.Formsのプロジェクトに下記を追加。

https://www.nuget.org/packages/xcc


次に、.csprojを生で開いて、以下のようにXccRemoveIgnorableContentの設定を追加。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
...
    <XccRemoveIgnorableContent>True</XccRemoveIgnorableContent>
  </PropertyGroup>
...

これで、XamlParseExceptionは発生しなくなります(`・ω・´)


なお、XamarinのBugzilla見る限りでは本家でもそのうち対応するっぽいですが、それまでの間はこの方法で。

2016-04-03

[]Windows 10 IoTNFCコントローラー(PN7120)を使う

Raspberry Piに入れたWindows 10 IoTNFCコントローラーを使ってみました(・ω・)

使ったのはこれ。


PN7120 NFC Controller SBC Kit

http://akizukidenshi.com/catalog/g/gM-10012/


Hardware Compatibility Listにものっています。

https://ms-iot.github.io/content/en-US/win10/SupportedInterfaces.htm#NFC


っで、インストール手順の詳細はpdfを参照ですが、まずは以下のドライバダウンロード


PN7120 on Windows IoT SW package

http://www.nxp.com/documents/software/SW349710.zip


解凍してacpitabl.dat、NXPPN7120.infをC:\Windows\System32にコピー。

PowerShellからEnter-PsSessionでラズパイに接続、以下のコマンドを実行して再起動すればドライバが使えるようになります。

> devcon dp_add C:\Windows\System32\NXPPN7120.inf

…ハズなんですが、どうもこのドライバが10.0.10240にしか対応しておらず、10.0.10586(現時点のもの)だと正しく動作してくれません(´・ω・`)

ちょっと調べたところ、infファイルを別途用意している例があったので、それを使ってみることにしました。

https://social.msdn.microsoft.com/Forums/ja-JP/d9b2e4df-cb64-4f50-a9e3-9fa2d83be14c/nxp-om5577-nfc-board-driver-broken-on-10586?forum=WindowsIoT


っで、infファイルの書き換え版を使用したところ、ドライバーも正しく認識。

f:id:machi_pon:20160403154457p:image


Proximity APIにより、交通系カードを使ってArrived/Departedの確認はできました(・∀・)

けど、SmartCard APIは使えないのかな(´・ω・`)

2016-02-02

[]未だにCompact Frameworkの開発とかしてるやつ

とりあえずコードの断捨離として、GitHubに旧コードをこっそり置いてあったりします(・ω・)

https://github.com/usausa/Smart-Net-CE

概要

  • ぎょーむアプリを作る際に使用するCompact Frameworkの機能を補うためのものです
  • SDF(Smart Device Framework)とかを使っているのであればいらないかもしれません
  • もともと、SDFの一部前身がMSDNの記事にあったりしたころから、それを参考に作った機能もあるので
  • CE用Smart.Navigationの最新版も含まれています(この版、あまり気に入っていないけど)
  • 実は完全版ではありません、以下の機能は気が向いたらFixして追加するかもしれません
  • HT開発にありがちな固定長ファイル操作、一覧表示用Control、通信系機能は、実装が複数あったり不完全な箇所があったりするので除外しています
  • Reactive、Functionalな機能も、同様の理由で除外しています、好き嫌いもあるし

まあ、気が向いたら手を入れるかも(´・ω・`)

2016-01-29

[]WPFでWindowStyle="None" ResizeMode="NoResize"なWindowを作るとドロップシャドウがつかなくなる件について

WPF小ネタ。


っで、この件に関するお手軽な対策としては、Border.EffectにDropShadowEffectを使用してWindowの縁部分のドロップシャドウを描画すること。

このためには、Window自体は透明にして、内部のGridに背景色を設定します。


処理を抜粋するとこんな感じ。

<Window x:Class="Example.WpfApplication.Views.HogeWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="OperationWindow"
        AllowsTransparency="True" WindowStyle="None" ResizeMode="NoResize" BorderThickness="3">
    <Border>
        <Border.Effect>
            <DropShadowEffect BlurRadius="5" Color="Black" Opacity="0.8" ShadowDepth="0.5" />
        </Border.Effect>
        <Grid Background="White">
...
        </Grid>
    </Border>
</Window>

っで、サンプルソースを以下においておきます。

https://github.com/usausa/Example-Net-WPF-BorderlessWindow


このサンプルでは、MainWindowのタイトルバーにある[Operation]、[Result]ボタンを押すと、それぞれDropShadowEffectのついたWindowStyle="None" ResizeMode="NoResize"な子Windowが表示されます。

f:id:machi_pon:20160129113534p:image

2016-01-11

[]InspectCode(ReSharper Command Line Tools)をJenkinsで使う

あまりInspectCodeの話を見ないので書いておきます(・ω・)

入手先は以下。

https://www.jetbrains.com/resharper/features/command-line.html

これはなんぞ?

ReSharperコマンドラインで実行できるツールです。

実行結果をファイルに保存し、その内容をJenkinsで表示したり出来ます。

単体での使い方

以下のように実行すると、results.xmlに実行結果が保存されます。

InspectCode.exe Example.sln /o=results.xml /no-swea

オプションの詳細は下記あたりを参照してください。

https://confluence.jetbrains.com/display/NETCOM/Introducing+InspectCode

Jenkinsでの使い方

警告の表示にはWarnings Pluginを使用します。

設定としては、まず[ビルド]から[Windowsバッチコマンドの実行]でIncpectCode.exeの実行を行います。

次に、[ビルド後の処理]で[コンパイラの警告の集計]を追加し、[ワークスペースのファイルをスキャンする]で[集計するファイル]にInspectCodeの出力ファイルを設定、[パーサー]には「Resharper InspectCode」を選択します。

f:id:machi_pon:20160111164455p:image

っで、ビルド実行がされると、IncpectCodeの結果も集計されるようになります。

f:id:machi_pon:20160111164456p:image

.NETアプリの静的チェックについては、StyleCop.Analyzersによるコードスタイルのチェック、コード分析によるチェックに加え、InspectCodeによるチェックが基本三点セットというところでしょうか(・ω・)?

2015-12-18

[]ウエッブ屋さんなんかにはまったく関係ないけどエッスアイヤーではまだお金も貰えたりもする情報処理技術者試験、ST取ってコンプ(・ω・)?

正直、もうモチベーションも糞もなかったんですが(´・ω・`)ITストラテジスト合格して、コンプしたので一応、完結編パピコ。

まあ、正確に言うと自分が持っているのはSCではなく昔のSVなんですが、会社からお金が貰えないという意味では同じもの扱いしておきます。


っで、過去編インデックスは以下。

中身がある日記はSAのくらいですが。

ST自体について?

とりあえず、普段コードよりもパワポを書いている人の方が受かりやすいんじゃないでしょうか(・ω・)?、つって。


あと、どうせなのでついでにこれも。

わい、ベンダーの試験ってこれしか受験したことない(・ω・;)

2015-11-13

[]Windows.Devices.SmartCards名前空間のクラスとPaSoRiFeliCaカードを使う(`・ω・´)

Windows 10上で動くUWPアプリからWindows.Devices.SmartCards名前空間のクラスを用いて、PaSoRiFeliCaカードを使う方法について。

使うPaSoRiはこれ。

SONY 非接触ICカードリーダー/ライター PaSoRi(パソリ) USB対応 RC-S380

SONY 非接触ICカードリーダー/ライター PaSoRi(パソリ) USB対応 RC-S380

っで、リーダーに送信するコマンドの構築を容易にするために、下記を利用するのでソースをダウンロードしておきます。

NFC Smart Card Reader PC/SC Library


上記の準備ができたら、Visual Studio 2015でUWPのプロジェクトを作成。

PC/SC Libraryのソースについては、とりあえずPcScSdkフォルダのソースを自分のプロジェクトに追加しちゃいましょうか。

また、プロジェクトから「Package.appxmanifest」を開き、[機能]の[共有ユーザー証明書]をチェックしておきます。


っで、ここまでできればPaSoRiFeliCaカードを使う準備は完了、後は以下のようなコードでOKだよもん。

using Windows.Devices.Enumeration;
using Windows.Devices.SmartCards;
using Windows.Networking.Proximity;

using Felica;

private async void Polling()
{
    // Reader検索
    var selector = SmartCardReader.GetDeviceSelector(SmartCardReaderKind.Any);
    var devices = await DeviceInformation.FindAllAsync(selector);
    var device = devices.FirstOrDefault();
    if (device == null)
    {
        return;
    }

    var reader = await SmartCardReader.FromIdAsync(device.Id);
    if (reader == null)
    {
        return;
    }

    // カード検索
    var cards = await reader.FindAllCardsAsync();
    var card = cards.FirstOrDefault();
    if (card == null)
    {
        return;
    }

    // 接続してポーリングコマンド送信
    using (var con = await card.ConnectAsync())
    {
        var handler = new AccessHandler(con);

        var result = await handler.TransparentExchangeAsync(new byte[] { 6, 0, 0xff, 0xff, 0, 3 });
    }
}

送ってるコマンドは特に説明しないので知らない人はFeliCaの資料でも見てくんろ。

っで、ポーリングが成功すれば、resultには18byte(サイズ 1byte + コマンド 1byte + IDm 8byte + PMm 8byte)の応答が返ってきます。


楽ちんぽ(・∀・)


なお、UWPアプリじゃなくてデスクトップアプリでも、TargetPlatformVersionを設定してWindows.winmdとSystem.Runtime.WindowsRuntime.dllを参照すればWindows.Devices.SmartCardsの使用は可能で、その場合は「Package.appxmanifest」の部分の作業は不要です。