6

私のプロジェクトでは、多くのISerializers実装をアセンブリスキャナーに登録しています。FWIWこれは私の登録するコードですISerializers

Scan(scanner =>
{
    scanner.AssemblyContainingType<ISerializer>();
    scanner.AddAllTypesOf<ISerializer>().NameBy(type => type.Name);
    scanner.WithDefaultConventions();
});

その後、正しく登録されます

ISerializer (...ISerializer)
Scoped as:  Transient

JsonSerializer    Configured Instance of ...JsonSerializer
BsonSerializer    Configured Instance of ...BsonSerializer

などなど。

現在、必要なシリアライザーを解決する方法を理解できた唯一の方法は、サービスロケーションコールをハードコーディングすることです。

jsonSerializer = ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");

クラスでjsonSerializerが特に必要であることがわかったので、ISerializerがプロパティ名に基づいて名前付きインスタンスを接続するように指示するルールなどを構成する方法はありますか?私が持つことができるように

MySomeClass(ISerializer jsonSerializer, ....)

そしてStructureMapはこのシナリオを正しく解決しますか?または、私はこれに間違ってアプローチしていますか?おそらく、ISerializerを実装する具体的なタイプを登録してから、具体的に使用する必要があります

MySomeClass(JsonSerializer jsonSerializer, ....)

具体的なクラスでこれらの線に沿った何かのために?

4

4 に答える 4

5

依存性注入を実行していて、特定のインターフェイスの特別に型指定されたインスタンスを作成できる必要がある場合、推奨される解決策は、特別なファクトリクラスを作成することです。これにより、実際にコンテナを挿入しなくても、名前付き引数を使用できます。

これは、注入する抽象型です。

public interface ISerializerFactory
{
    ISerializer GetSerializer(string name);
}

コンテナ(StructureMap)を利用する具体的なタイプは次のとおりです。

public class StructureMapSerializerFactory : ISerializerFactory
{
    public ISerializer GetSerializer(string name)
    {
        return ObjectFactory.GetNamedInstance<ISerializer>(name);
    }
}

その場合、クラスは次のようになります。

public class MyClass
{
    private readonly ISerializerFactory serializerFactory;

    public MyClass(ISerializerFactory serializerFactory)
    {
        if (serializerFactory == null)
            throw new ArgumentNullException("serializerFactory");
        this.serializerFactory = serializerFactory;
    }

    public string SerializeSomeData(MyData data)
    {
        ISerializer serializer = serializerFactory.GetSerializer("Json");
        return serializer.Serialize(data);
    }
}

自動的に機能しない「JsonSerializer」の代わりに、この通過する「Json」を作成しました。ただし、登録名を変更して、冗長な「Serializer」サフィックスを削除する必要があると思います(を要求しているため、これがシリアライザーであることがすでにわかっていますISerializer)。つまり、次のようなメソッドを作成します。

private static string ExtractSerializerName(Type serializerType)
{
    string typeName = serializerType.Name;
    int suffixIndex = typeName.IndexOf("Serializer");
    return (suffixIndex >= 0) ?
        typeName.Substring(0, suffixIndex - 1) : typeName;
}

そして、次のように登録します。

scanner.AddAllTypesOf<ISerializer>().NameBy(type => ExtractSerializerName(type));

次に、「JsonSerializer」の代わりに文字列「Json」を使用して作成できます。これにより、見た目が少し醜くなり、結合が少なくなります。

ハードコードされた文字列が気に入らない場合は、ファクトリの列挙型を作成することもできます。

public enum SerializationFormat { Json, Bson, Xml };

public interface ISerializerFactory
{
    ISerializer GetSerializer(SerializationFormat format);
}

public class StructureMapSerializerFactory : ISerializerFactory
{
    public ISerializer GetSerializer(SerializationFormat format)
    {
        return ObjectFactory.GetNamedInstance<ISerializer>(format.ToString());
    }
}

したがって、これを書く代わりに:

ISerializer serializer = serializerFactory.GetSerializer("Json");

代わりにこれを書くことができます:

ISerializer serializer =
    serializerFactory.GetSerializer(SerializationFormat.Json);

長期的には、エラーが発生しにくくなります。

シリアライザーのクラス名を変更し始めたり、名前に一貫性がない場合は、単純なToString()ものをswitchステートメントに置き換えて、実際に列挙値をクラス名にマップできるため、これはおそらく長期的にはより保守しやすくなります。再登録します。

おそらく、このコードのすべて(質問の自動登録コードを含む)を同じ名前空間、または同じコードファイルに入れて、これらの部分がすべて相互に依存していることを明確に示します。

于 2010-04-07T15:44:39.307 に答える
2

私の知る限り、それは実際にはアセンブリスキャン機能の目的ではありません。単一のアセンブリにさまざまなインターフェイス(たとえばIRepository<File>IRepository<Folder>など)の実装が多数ある場合に便利です。したがって、たとえば、テストアセンブリを参照しているときは、テストリポジトリを注入し、本番環境にいるときは、EntityFrameworkリポジトリを注入しています。

あなたの場合、どの例も完全に依存関係を注入しているようには見えません。言い換えれば、あなたが書くとき

ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");

文字列をハードコーディングすることで、Jsonシリアライザーに引き続き依存し、StructureMapがその呼び出しから他の種類のシリアライザーを返すことは意味がありません。

StructureMapで何を達成するのか正確にはわかりませんが、特定の実行時条件のセットに応じて特定のシリアライザーを返す必要がある場合は、条件付き構文を調べることができます。

一方で、そのようなスイッチがここでやろうとしているようには思えないので、絶対にそれを取り除くことを検討する必要があります。結局のところ、上記のコードは実際には

new JsonSerializer();

StructureMapはすばらしいツールですが、すべてのプロジェクトに必要なわけではありません。

幸運を!

于 2010-03-28T09:33:22.597 に答える
1

気になります。ISerializer自体が付加する価値は何ですか?特定の実装から、実行時に選択される1つまたは複数の実装に移りましょう。

タイプが特定のタイプのシリアライザーに依存している場合は、それに依存します(IJsonSerializer)。これには、そのタイプのデフォルトインスタンスがコンテナに登録されている必要があります。

ただし、戦略としてISerializersを使用することを検討している場合は、すべてのISerializerを登録してから、それらの配列に依存するようにすると、StructureMapは登録されているすべてのISerializerの配列をプッシュします。これらのシリアライザーを使用するクラスは、使用するシリアライザーを選択する責任があります。

戦略シナリオでは、調整クラスがそれらを区別するために使用するシリアライザーのメタデータが必要になる可能性があります。私見ですが、これは登録された型の名前のようなコンテナ構成ではなく、実装自体のメタデータである必要があります。

于 2010-04-07T15:24:50.923 に答える
1

コードはJsonSerializerを取得していると想定しているため、JsonSerializerのみが実装する新しいIJsonSerializerインターフェイスを作成します。JsonSerializerを必要とするクラスは、IJsonSerializerを受け入れる必要があります。ISerializerインターフェイスをすべてのシリアライザーに共通にする必要がある場合は、IJsonSerializerをマーカーインターフェイスとして使用できます。

または、StructureMapにクラスを登録するときに、特定のISerializer実装をクラスに関連付けることができます。

x.For<MySomeClass>().Use(c => new MySomeClass(c.GetInstance<JsonSerializer>()));
于 2010-03-29T13:36:55.470 に答える