2016-09-01 15 views
2

のは、私はこのようになりますコントローラのアクションがあるとしましょう:model.Saveが仕事を得るためにはasp.netコアのモデルに依存関係を挿入する方法は?

[HttpPost] 
public async Task<IActionResult> Add([FromBody] MyModel model){ 
    await model.Save(); 
    return CreatedAtRoute("GetModel", new {id = model.Id}, model); 
} 

を、それはいくつかの依存関係が必要です。今のよう

public class MyModel{ 
    private readonly ApplicationDbContext _context; 
    public MyModel(ApplicationDbContext context){ 
     _context = context; 
    } 
    public async Task Save(){ 
     // Do something with _context; 
    } 
} 

を、コンテキストがでnullですMyModelのコンストラクタです。どのように私はそれを注入できますか?私は、コントローラにサービスを注入し、私のモデルでそのような操作を実行できることを知っていますが、オブジェクト指向のアプローチを貧血ドメインモデルよりも使用したいのですが?それは単に不可能ですか?

+0

はhttp://stackoverflow.com/questions/39240231/is-there-a-better-way-than-this-to-inject-a-function-into-an-を見てみましょう39244267#39244267 –

答えて

2

DTOのほかに、あなたはまた、豊富なモデルを使用することがあります。)あなたのデータを保存します。あなたはモデルにctor注入を世話するカスタムモデルバインダーが必要です。

組み込みのモデルバインダーは、デフォルトのctorを見つけることができないと不平を言っています。したがって、あなたはカスタムを必要とします。

類似した問題の解決方法が見つかるかもしれませんhere、登録されたサービスを調べてモデルを作成します。

以下のスニペットは、あなたの特定のニーズを満たすうえで、わずかに異なる機能を提供することに注意してください。以下のコードは、ctor注入のモデルを想定しています。もちろん、これらのモデルには、定義した通常のプロパティがあります。これらの特性は正確に予想通りに記入されているため、のモデルを結合するときのボーナスはの正しい挙動になります。ここではバインダーが登録される方法です

public class DiModelBinderProvider : IModelBinderProvider 
{ 
    public IModelBinder GetBinder(ModelBinderProviderContext context) 
    { 
     if (context == null) { throw new ArgumentNullException(nameof(context)); } 

     if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType) 
     { 
      var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder); 
      return new DiModelBinder(propertyBinders); 
     } 

     return null; 
    } 
} 

services.AddMvc().AddMvcOptions(options => 
{ 
    // replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider 
    var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider)); 
    var binderIndex = options.ModelBinderProviders.IndexOf(provider); 
    options.ModelBinderProviders.Remove(provider); 
    options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider()); 
}); 

私は新しいバインダーが正確に登録されなければならない場合には、非常によく分からない

public class DiModelBinder : ComplexTypeModelBinder 
    { 
     public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders) 
     { 
     } 

     /// <summary> 
     /// Creates the model with one (or more) injected service(s). 
     /// </summary> 
     /// <param name="bindingContext"></param> 
     /// <returns></returns> 
     protected override object CreateModel(ModelBindingContext bindingContext) 
     { 
      var services = bindingContext.HttpContext.RequestServices; 
      var modelType = bindingContext.ModelType; 
      var ctors = modelType.GetConstructors(); 
      foreach (var ctor in ctors) 
      { 
       var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList(); 
       var parameters = paramTypes.Select(p => services.GetService(p)).ToArray(); 
       if (parameters.All(p => p != null)) 
       { 
        var model = ctor.Invoke(parameters); 
        return model; 
       } 
      } 

      return null; 
     } 
    } 

このバインダーはによって提供されます同じインデックスで、これを実験することができます。

そして、最後に、これはあなたがそれを使用する方法である。

public class MyModel 
{ 
    private readonly IMyRepository repo; 

    public MyModel(IMyRepository repo) 
    { 
     this.repo = repo; 
    } 

    ... do whatever you want with your repo 

    public string AProperty { get; set; } 

    ... other properties here 
} 

Modelクラスは、(既に登録)サービスを提供するバインダーによって作成され、モデルバインダーの残りの部分が提供されますそれらの通常のソースからのプロパティ値。

HTH

+0

私は同様のコードを試して、コントローラの動作で[FromBody]を追加するまで動作します。私はこの行にあるようだ 'var propertyBinders = context.Metadata.Properties.ToDictionary(property => property、context.CreateBinder);' BindingSourceに関する情報が失われました。 – Igor

+0

明示的な[FromBody]装飾が必要なのはなぜですか? –

0

あなたはDTO(データ転送オブジェクト)パターンを使用してコードをリファクタリングするために検討すべきサービス

3

を提供するために、サービスを構成する方法でサービスを追加してみました。単に

  • MyModelは、プロパティ/計算されたプロパティを含むデータコンテナのみである必要があります。 Save()方法から
  • ロジックはApplicationDbContextのような依存関係について知っておくべきこと、ModelRepositoryのように別々のクラスに抽出する必要があります。

    public class ModelRepository 
    { 
        private readonly ApplicationDbContext _context; 
    
        public ModelRepository(ApplicationDbContext context) 
        { 
         _context = context; 
        } 
    
        public async Task Save(MyModel model) 
        { 
         // Do something with _context; 
        } 
    } 
    
  • 最後に、あなたのコントローラがビルドでDI使用してそれを解決する(ModelRepositoryのインスタンスを使用する必要がありますまあ、

    [HttpPost] 
    public async Task<IActionResult> Add([FromBody] MyModel model) 
    { 
        await _modelRepository.Save(model); 
        return CreatedAtRoute("GetModel", new {id = model.Id}, model); 
    } 
    
関連する問題