ちょっとした特殊な魔法

前の記事で、 IQuery実装を受け取るためのファクトリーの例を挙げましたが、その操作のメカニズムについては説明しませんでした。

_queryFactory.GetQuery<Product>() .Where(Product.ActiveRule) .OrderBy(x => x.Id) .Paged(0, 10) //  10     //        ElasticSearch,  : _queryFactory.GetQuery<Product, FullTextSpecification>() .Where(new FullTextSpecification(«»)) .All() //  EF          Dapper _queryFactory.GetQuery<Product, DictionarySpecification, DapperQuery>() .Where(new DictionarySpecification (someDirctionary)) .All()
      
      





この記事では、合意に基づいてアセンブリの必要なコンポーネントを登録する手法を共有したいと思います。 今、私は手元に異なるCQRS実装を持つコードベースを持っているので、例は異なります。 これは基本的なものではありません。基本的な考え方は変わりません。



ListParamsがフロントエンドから来る仕様であるインターフェースがあるとしましょう

 public interface IListOperation<TDto> { ListResult<TDto> List(ListParams listParam); }
      
      





挑戦する

アプリケーション開発者がコントローラー、プロジェクション、およびサービスを作成するのを防ぎます。





解決策

リスト操作の基本クラスを作成します。

  public class ListOperationBase<TEntity, TDto> : IListOperation<TDto> where TEntity: IEntity where TDto: IHaveId { protected readonly IDbContext DbContext ; public ListOperationBase(IDbContext dbContext ) { if (dbContext == null) throw new ArgumentNullException(nameof(dbContext)); DbContext = dataStore; } public virtual ListResult<TDto> List(ListParam listParam) { var data = AddProjectionBusinessLogic(AddEntityBusinessLogic(DataStore .GetAll<TEntity>()) .ProjectTo<TDto>()) .Filter(listParam); return new ListResult<TDto>() { Data = data .Paging(listParam) .ToList(), TotalCount = data.Count() }; } protected virtual IQueryable<TEntity> AddEntityBusinessLogic(IQueryable<TEntity> queryable) => queryable; protected virtual IQueryable<TDto> AddProjectionBusinessLogic(IQueryable<TDto> queryable) => queryable; }
      
      





ProjectToメソッドは、合意に基づいて予測を作成できるAutoMapper機能です。 フォームの鈍いSelect構造を記述することを避けながら、 エンティティ全体をメモリに上げる必要がなくなります。

 Query.Select(x => { Name = x.Name, ParentUrl = x.Parent.Url, Foo = x.Foo })
      
      





仮想メソッドAddEntityBusinessLogicおよびAddProjectionBusinessLogicを使用すると、投影を作成する前後にフィルタリング条件を追加できます。



迅速なプロトタイピングのために、 ListOperationBase <TEntity、TDto>を使用できます。実際の実装では、正しいロジックで実際の操作を作成する必要があります。 これを行うには、アプリケーションの開始時に、合意によりアセンブリ内にあるすべてのものを登録する必要があります。 私の場合、モジュール式アーキテクチャが使用されており、これがモジュール読み込みコードです。 モノリシックアプリケーションの場合は、タイプをロードするアセンブリのリストも作成する必要があります。

 var types = GetType().Assembly.GetTypes(); var operations = types .Where(t.IsClass && !t.IsAbstract && t.ImplementsOpenGenericInterface(typeof(IListOperation<>))); foreach (var operation in operations) { var definitions = operation.GetInterfaces().Where(i => i.ImplementsOpenGenericInterface(typeof (IListOperation<>))); foreach (var definition in definitions) { Container.Register(definition, operation); } // ... }
      
      





すべてのCrud操作に必要なコントローラーは1つだけです。 github.com/hightechtoday/costeffectivecode/blob/master/src/CostEffectiveCode.WebApi2/WebApi/Infrastructure/RuntimeScaffoldingHttpControllerSelectorで汎用WebApiコントローラーのControllerSelectorの実装を見つけることができます。



 public ListResult<TListDto> List(ListParam loadParams) => (_container.ResolveAll<IListOperation<TListDto>>().SingleOrDefault() ?? new ListOperationBase<TEntity,TListDto>(DataStore)) .List(loadParams);
      
      





コンテナーをコントローラーに渡すことは、もちろんアイデア( ServiceLocator )であり、実際、ファクトリーメソッドで呼び出しをラップする方がはるかに優れています( QueryFactoryの例で行われているように)。 もう1つの弱点は、同じタイプの2つのIListOperation実装が登録されている場合の対処方法です。 この質問に対する明確な答えはありません。それはすべて、アプリケーションの詳細とシステム要件に依存します。



その結果、ラピッドプロトタイピング用のシステムが得られ、プログラマーがコントローラーを記述したり、コンテナーにサービスを登録したりする必要がなくなりました。 必要なのは、エンティティ、DTOを追加し、マッピングを記述することだけです。 AutoMapperを使用する場合、 Mapper.AssertConfigurationIsValid()コンストラクトを必ず追加する必要があります EntityまたはDtoを変更する必要がある場合、エラーについて知るのに役立ちます。 ところで、操作の登録との類推により、すべてのマッピングが明白な場合の合意の下でマッピングの作成を自動化することが可能です。 ただし、実際には、マッピングに複数の行をかなり頻繁に追加する必要があるため、数行しかないため、これを手動で行うことを好みます。



ステップバイステップ

  1. SomeEntityの追加:IEntity
  2. SomeEntityListDtoを追加
  3. SomeEntity-> SomeEntityListDtoのマッピングを登録します
  4. / SomeEntity / Listメソッドを自動的に取得します
  5. SomeEntityListOperation <SomeEntity、SomeEntityListDto>にビジネスロジックを追加します
  6. / SomeEntity / Listメソッドは、「正しい」ビジネスロジックで新しい実装を使用し始めます


エンティティをプレゼンテーションレイヤーに渡すことができる場合は、マッピングを省略できます。



All Articles