web-dev-qa-db-ja.com

未登録タイプのSimpleInjector force Lifestyle.Transient

以下のようなラッパークラスがあります。

// this is not used for IoC purposes, but by code to cast object to known T
internal interface ITaskCompletionSourceWrapper
{
    void SetResult(object result);
    /* snip */
}

internal class TaskCompletionSourceGenericWrapper<TResult> : ITaskCompletionSourceWrapper
{
    readonly TaskCompletionSource<TResult> tcs;
    public TaskCompletionSourceGenericWrapper() =>        
        this.tcs = new TaskCompletionSource<TResult>(TaskCreationOptions.RunContinuationsAsynchronously);
    public void SetResult(object result) => tcs.SetResult((TResult)result);
    /* snip */
}

現在Activatorを使用してインスタンス化している場合:

var responseType = request
                     .GetType()
                     .GetInterfaces()
                     .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>))
                     GetGenericArguments()[0];

var wrapperType = typeof(TaskCompletionSourceGenericWrapper<>).MakeGenericType(responseType);
var tcsWrapper = this.container.GetInstance(wrapperType) as ITaskCompletionSourceWrapper;

私はこれをSimpleInjectorに切り替え(ロギングやその他のIOC goodnessを使用する場合があるように)、問題が見つかるまで(ライフスタイルが共有されていることが原因)、すべて順調であると思いました。

/* snip responseType code from above */
var wrapperType = typeof(TaskCompletionSourceGenericWrapper<>).MakeGenericType(responseType);
var tcsWrapper = this.container.GetInstance(wrapperType) as ITaskCompletionSourceWrapper;

// i can see the type has been registered as scoped, but we need it to be unique
[30] = ServiceType = TaskCompletionSourceGenericWrapper<Object>, Lifestyle = Async Scoped

上記はAsyncScopedLifestyle.BeginScopeブロック内で呼び出されています。

SimpleInjectorを使用して、実行時にこのラッパーをインスタンス化したいと思います(ただし、Lifestyle.Transientを確認してください)。

ラッパーのタイプは、intstringboolなどからほとんど任意ですが、単純なPOCOクラスの場合もあります。

これは、オープンジェネリック登録の場合と似ていますが、他の開発者が単純な型のアセンブリを指定することは避けたいです(確かに、忘れてしまうことと、クラスが単純であること(コマンド/クエリPOCOなど))。

どうすれば上記を達成できますか、それともコレクションの登録を強制されますか?

または、これはライフスタイルの不一致により深くなります-ラッパーには依存関係がないため(おそらくILogger以外は)単純なクラスのインスタンス化のみを実行しているためです(コンポーネントのIoCスタイルを分離するのではなく、Activatorを置き換えたい)。 。

4
morleyc

彼らのドキュメント Batch-Registration/Auto-Registration と経験に基づいて、依存関係注入を使用する場合は、インターフェースの実装を登録する必要があります。

var container = new Container();
// If related classes exist in a different Assembly then add them here
var assemblies = new[] { typeof(TaskCompletionSourceGenericWrapper<>).Assembly };

container.Collection.Register(typeof(TaskCompletionSourceGenericWrapper<>), assemblies, Lifestyle.Transient);

DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

私が考えることができるがテストしていないハックの1つは、ラッパーのディープコピーa.k.aクローンを返すラッパークラスを作成することです。オブジェクトのクローンを返すことは、リクエストするたびに新しいインスタンスを返すという点で一時的なものに似ていますが、このアプローチの欠点は、シリアル化可能なクラスに対してのみ実行できるため、[Serializable()]を追加する必要がある場合があることです。 。詳細は ディープクローニングオブジェクト を参照してください。

public interface IUglyDuckling
{
    ITaskCompletionSourceWrapper GetITaskCompletionSourceWrapper(Type type);
}
public class UglyDuckling : IUglyDuckling
{
    public ITaskCompletionSourceWrapper GetITaskCompletionSourceWrapper(Type type)
    {
        var wrapperType = typeof(TaskCompletionSourceGenericWrapper<>).MakeGenericType(type);
        var tcsWrapper = this.container.GetInstance(wrapperType) as ITaskCompletionSourceWrapper;
        return ObjectCopier.Clone<ITaskCompletionSourceWrapper>(tcsWrapper);
    }
}
4
jegtugado