8

データアクセス用のリポジトリを備えた WebAPI + Autofac + Automapper を使用しています。モデルをドメイン エンティティにマップする必要があります。具体的には、ID 値を実際のエンティティに変換する必要があります。大したことではありませんよね?私はこれを MVC で問題なく実行しました。本質を明らかにするために、私がやっていることを単純化します。

public class EntityConverter<T> : ITypeConverter<int, T>
        where T : Entity
{
   public EntityConverter(IRepository<T> repository)
   {
       _repository = repository;
   }

   private readonly IRepository<T> _repository;

   public T Convert(ResolutionContext context)
   {
       _repository.Get((int) context.SourceValue);
   }
}

リポジトリは Autofac に登録されInstancePerApiRequest、セッション/トランザクション管理のため、リポジトリとして管理されます。したがって、コンバーターを同じスコープに登録する必要があります。

 builder.RegisterGeneric(typeof(EntityConverter<>))
        .AsSelf()
        .InstancePerApiRequest();

Automapper の設定は次のようになります。

 var container = builder.Build(); // build the Autofac container and do what you will

 Mapper.Initialize(cfg => {
      cfg.ConstructServicesUsing(container.Resolve); // nope nope nope
      // configure mappings
      cfg.CreateMap<int, TestEntity>().ConvertUsing<EntityConverter<TestEntity>>()
});
Mapper.AssertConfigurationIsValid();

だからここが悪い部分です。Automapper では、ConstructServicesUsingconfig をビルドする前に、guy を設定する必要があることを理解しています。後で設定すると、使用されなくなります。containerがルート スコープであるため、上記の例は機能しません。解決しようとするEntityConverter<TestEntity>と、Autofac は、要求された型が別のスコープに登録されていると不平を言います。理にかなっています。WebApi によって作成されたスコープが必要です。

少し間を置いて、WebApi の依存性注入に関する 1 つの事実を説明させてください (これは Autofac 固有のものではないと思います)。WebApi はIDependencyScopeリクエストの を作成し、HttpRequestMessage.Properties. HttpRequestMessage同じインスタンスにアクセスできない限り、元に戻すことはできません。したがって、私のAsInstancePerApiRequestスコープIRepositoryと私のコンバーターはそれに依存していIDependencyScopeます。

つまり、それこそが問題の本質であり、私は MVC とのこの違いに本当に不満を感じていました。あなたはできません

 cfg.ConstructServicesUsing(GlobalConfiguration.Configuration.DependencyResolver.GetService);

を使用するのと同じcontainer.Resolveです。使えない

 GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetService

A)実際に必要なスコープの隣に新しいスコープを作成するため、B)作成した新しいスコープを実際にクリーンアップできないからです。Service Locator の使用は、同じ問題を解決するための新しい方法です。WebApi が使用しているスコープに到達できません。私のコンバーターとその依存関係が単一のインスタンスまたは依存関係ごとのインスタンスである場合、それは問題にはなりませんが、そうではなく、それを変更すると、さらに多くの問題が発生します。

これで、Autofac を使用して AutoMapper 構成を作成し、それを単一のインスタンスとして登録できます。リクエストごとのIMappingEngineインスタンスを作成することもできます。しかし、サービス コンストラクターが、現在のスコープにアクセスできない、最初に登録した単一のデリゲートを常に使用する場合、それは私には何の役にも立ちません。マッピング エンジンのインスタンスごとにデリゲートを変更できれば、うまくいくかもしれません。しかし、私はできません。

それで、私は何ができますか?

4

3 に答える 3

7

別のオプション (今回は組み込み) は、マップごとのオプションを使用することです。

Mapper.Map<Source, Destination>(dest, opt => opt.ConstructServicesUsing(type => Request.GetDependencyScope().GetService(typeof(YourServiceTypeToConstruct))));

マッピング構成でグローバル IoC 構成を設定する必要はありません。

もう 1 つのオプションは、IoC ツールを使用して MappingEngine をインスタンス化する方法を構成することです。

public MappingEngine(
    IConfigurationProvider configurationProvider,
    IDictionary<TypePair, IObjectMapper> objectMapperCache,
    Func<Type, object> serviceCtor)

1 つ目は単なる Mapper.Configuration で、2 つ目はおそらくシングルトンである必要があり、3 つ目は現在のネストされたコンテナーの解像度を入力できます。これにより、毎回 Map オーバーロードを呼び出す必要がなくなります。

于 2015-01-13T14:25:55.367 に答える
1

これはあなたに適している場合とそうでない場合があります..しかし、ここに行きます:

私たちは最近、MVC のモデル バインダーに対してこれを行いました。私たちのモデル バインダー (GET リクエスト上) は、Ninject マネージド サービスを使用してモデルを構築するようになりました。

基本的に、ファクトリを (Ninject の Factory 拡張機能を使用して..おそらく Autofac にも同様のものが存在する) 「AutomapperBootstrapper」クラスに注入します。これにより、Automapper マッピングが作成され、AutomapperProfileに追加されます。ややこのように:

Mapper.Initialize(cfg =>
{
    cfg.AddProfile(_factory.CreateServiceViewModelMappingProfile());
    // etc..
});

mappingsProfile自体はMapFrom()、マッピングが発生するたびに評価される を使用します。このようなもの:

Mapper.CreateMap<Service, ServiceViewModel>()
            .ForMember(x => x.Regions,
                opt =>
                    opt.MapFrom(x => getRegions()))

private IEnumerable<Region> getRegions() {
    return _factory.CreateTheService().GetRegions();
}

モデル バインダーが起動されるたびに、Ninject はリクエストのすべての依存関係を接続し、すべてをフィルタリングします。

(興味のある方のために説明すると、このセットアップでは基本的にこれを行うことができます: /Area/Controller/Action/12、コントローラーのアクション メソッドは次のとおりです。

[HttpGet]
public ActionResult Action(ServiceViewModel model) {
    // ...
}

)。

于 2013-09-11T04:23:28.650 に答える