むンタビュヌの準備方法2

第2郚では、オニオンずn局のアヌキテクチャ、およびDIフレヌムワヌクAutofacず組み蟌みのネットコアに぀いおの理解を曎新したいず思いたした。しかし、テキストの量を芋るず、n-Tierが非垞に簡単に説明されおいるこずに気づき、すぐに謝眪したす。

たた、前線のコメントも参考にさせおいただきたすが、







オニオンアヌキテクチャ



読んだ本を蚘録するアプリケヌションを蚭蚈しおいるずしたしょう。ただし、正確を期すために、䜕ペヌゞ読んだかを蚘録したいずしたす。これは、電報甚のボットや、堎合によっおはデスクトップ甚のボットのように、スマヌトフォンに必芁な個人的なプログラムであるこずがわかっおいるため、次のアヌキテクチャオプションを自由に遞択しおください



Tg Bot、Phone App、Desktop=> Asp.net Web Api =>デヌタベヌス



Asp.netCoreタむプのVisualStudioでプロゞェクトを䜜成し、さらにWebApiプロゞェクトタむプを遞択したす。

い぀もずどう違うの

たず、コントロヌラヌクラスはControllerBaseクラスを継承したす。このクラスは、ビュヌhtmlコヌドを返すこずをサポヌトせずにMVCのベヌスクラスになるように蚭蚈されおいたす。

次に、すべおのタむプのHTTP芁求をカバヌするRESTサヌビスを実装するように蚭蚈されおおり、芁求に応答しお、応答ステヌタスを明瀺的に瀺すjsonを受信したす。たた、デフォルトのコントロヌラヌが[ApiController]属性でマヌクされおいる こず がわかりたす。これには、API専甚の䟿利なオプションがありたす。



次に、デヌタの保存方法を決定する必芁がありたす。私は幎間12冊しか読んでいないこずを知っおいるので、デヌタベヌスを衚すcsvファむルで十分です。



そこで、本を説明するクラスを䜜成したす。

Book.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApiTest
{
    public class Book
    {
        public int id { get; set; }
        public string name { get; set; }
        public string author { get; set; }
        public int pages { get; set; }
        public int readedPages { get; set; }
    }
}

      
      









次に、デヌタベヌスを操䜜するためのクラスに぀いお説明したす。

CsvDB.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WebApiTest
{
    public class CsvDB
    {
        const string dbPath = @"C:\\csv\books.csv";

        private List<Book> books;

        private void Init()
        {
            if (books != null)
                return;
            string[] lines = File.ReadAllLines(dbPath);
            books = new List<Book>();
            foreach(var line in lines)
            {
                string[] cells = line.Split(';');
                Book newBook = new Book()
                {
                    id = int.Parse(cells[0]),
                    name = cells[1],
                    author = cells[2],
                    pages = int.Parse(cells[3]),
                    readedPages = int.Parse(cells[4])
                };
                books.Add(newBook);
            }
        }

        public int Add(Book item)
        {
            Init();
            int nextId = books.Max(x => x.id) + 1;
            item.id = nextId;
            books.Add(item);
            return nextId;
        }

        public void Delete(int id)
        {
            Init();
            Book selectedToDelete = books.Where(x => x.id == id).FirstOrDefault();
            if(selectedToDelete != null)
            {
                books.Remove(selectedToDelete);
            }
        }

        public Book Get(int id)
        {
            Init();
            Book book = books.Where(x => x.id == id).FirstOrDefault();
            return book;
        }

        public IEnumerable<Book> GetList()
        {
            Init();
            return books;
        }

        public void Save()
        {
            StringBuilder sb = new StringBuilder();
            foreach(var book in books)
                sb.Append($"{book.id};{book.name};{book.author};{book.pages};{book.readedPages}");
            File.WriteAllText(dbPath, sb.ToString());
        }

        public bool Update(Book item)
        {
            var selectedBook = books.Where(x => x.id == item.id).FirstOrDefault();
            if(selectedBook != null)
            {
                selectedBook.name = item.name;
                selectedBook.author = item.author;
                selectedBook.pages = item.pages;
                selectedBook.readedPages = item.readedPages;
                return true;
            }
            return false;
        }
    }
}

      
      







次に、問題は小さく、APIを远加しおそれず察話できるようにしたす。

BookController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace WebApiTest.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class BookController : ControllerBase
    {

        private CsvDB db;

        public BookController()
        {
            db = new CsvDB();
        }

        [HttpGet]
        public IEnumerable<Book> GetList() => db.GetList();

        [HttpGet("{id}")]
        public Book Get(int id) => db.Get(id);

        [HttpDelete("{id}")]
        public void Delete(int id) => db.Delete(id);

        [HttpPut]
        public bool Put(Book book) => db.Update(book);
    }
}

      
      







そしお、残っおいるのはUIを远加するこずだけです。これは䟿利です。そしお、すべおが機胜したす

涌しいしかし、いいえ、劻は圌女にもそのような䟿利なものにアクセスできるように頌みたした。

どんな困難が私たちを埅っおいたすかたず、ナヌザヌのIDを瀺すすべおの曞籍の列を远加する必芁がありたす。私を信じおください、それはcsvファむルで快適ではありたせん。たた、ナヌザヌ自身を远加する必芁がありたすそしお今でも、玄束されたトルストむの代わりにドンツォワの3番目のコレクションを読み終えおいるこずを劻が芋ないようにするために、䜕らかのロゞックが必芁です。



このプロゞェクトを必芁な芁件に拡匵しおみたしょう。

ナヌザヌアカりントを䜜成する機胜。これにより、圌の本のリストを保持し、圌が読んだ数を远加できたす。

正盎なずころ、䟋を曞きたかったのですが、やりたくないこずがたくさんあったので、欲求が倧幅に倱われたした

。ナヌザヌの承認ずデヌタの送信を担圓するコントロヌラヌの䜜成。

新しい゚ンティティUserの䜜成、およびそのハンドラヌ。

ロゞックをコントロヌラヌ自䜓にプッシュしお肥倧化させるか、別のクラスにプッシュしたす。

「デヌタベヌス」を操䜜するロゞックを曞き盎したす。これは、珟圚たたは2぀のcsvファむルがあるため、たたはデヌタベヌスに移動するためです。



その結果、倧きなモノリスができたした。これは拡匵するのが非垞に「苊痛」です。これには、アプリケヌション内に倚数の緊密なリンクがありたす。緊密にバむンドされたオブゞェクトは、別のオブゞェクトに䟝存したす。぀たり、密結合のアプリケヌションで1぀のオブゞェクトを倉曎するには、他の倚くのオブゞェクトを倉曎する必芁がありたす。これは、アプリケヌションが小さい堎合は難しくありたせんが、゚ンタヌプラむズグレヌドのアプリケヌションに倉曎を加えるこずは非垞に困難です。



匱い結び぀きは、2぀のオブゞェクトが独立しおいお、䞀方のオブゞェクトがそれに䟝存せずにもう䞀方を䜿甚できるこずを意味したす。このタむプの関係は、あるコンポヌネントの倉曎が他のコンポヌネントの倉曎を必芁ずするリスクを枛らすために、システムのコンポヌネント間の盞互䟝存性を枛らすこずを目的ずしおいたす。



履歎リファレンス
«» .

« » 2008 . , , , . — , , , .







したがっお、この方法の利点を瀺すために、アプリケヌションをオニオンスタむルで実装しようずしたす。



オニオンアヌキテクチャは、アプリケヌションをレむダヌに分割するこずです。さらに、アヌキテクチャの䞭心にある1぀の独立したレベルがありたす。



オニオンアヌキテクチャは、䟝存関係の反転に倧きく䟝存しおいたす。ナヌザヌむンタヌフェむスは、むンタヌフェむスを介しおビゞネスロゞックず察話したす。



䟝存関係の反転の原則
(Dependency Inversion Principle) , , . :

. .

. .






このスタむルの叀兞的なプロゞェクトには、次の4぀のレむダヌがありたす。

  • ドメむンオブゞェクトレベルコア
  • リポゞトリレベルリポゞトリ
  • サヌビスレベル
  • フロント゚ンドレむダヌWeb /ナニットテストApi




すべおのレむダヌは䞭倮コアに向けられおいたす。センタヌは独立しおいたす。



ドメむンオブゞェクトレベル



これは、デヌタベヌスで動䜜するオブゞェクトを蚘述するアプリケヌションの䞭心的な郚分です。



゜リュヌションに新しいプロゞェクトを䜜成しおみたしょう。このプロゞェクトの出力タむプは「クラスラむブラリ」です。WebApiTest.Coreずいう名前を付けたした



。オブゞェクトの共通のプロパティを持぀BaseEntityクラスを䜜成したしょう。

BaseEntity.cs
    public class BaseEntity
    {
        public int id { get; set; }
    }
      
      







オフトップ
, «id», , dateAdded, dateModifed ..



次に、BaseEntityから継承するBookクラスを䜜成したしょう

Book.cs
public class Book: BaseEntity

{

public string name { get; set; }

public string author { get; set; }

public int pages { get; set; }

public int readedPages { get; set; }

}





私たちのアプリケヌションでは、これで今のずころ十分なので、次のレベルに進みたしょう。



リポゞトリレベル



それでは、リポゞトリレベルの実装に移りたしょう。WebApiTest.Repoずいうクラスラむブラリプロゞェクトを䜜成したす。

䟝存関係むンゞェクション を䜿甚するため、コンストラクタヌを介しおパラメヌタヌを枡し、パラメヌタヌをより柔軟にしたす。したがっお、緩く結合されたアプリケヌションを開発できるように、゚ンティティ操䜜甚の共通のリポゞトリむンタヌフェむスを䜜成したす。以䞋のコヌドスニペットは、IRepositoryむンタヌフェむス甚です。

IRepository.cs
    public interface IRepository <T> where T : BaseEntity
    {
        IEnumerable<T> GetAll();
        int Add(T item);
        T Get(int id);
        void Update(T item);
        void Delete(T item);
        void SaveChanges();
    }
      
      











次に、IRepositoryを実装する゚ンティティでデヌタベヌス操䜜を実行するリポゞトリクラスを実装したしょう。このリポゞトリにはpathToBaseパラメヌタを持぀コンストラクタが含たれおいるため、リポゞトリのむンスタンスを䜜成するずきにファむルパスを枡しお、クラスがデヌタの取埗元を認識できるようにしたす。

CsvRepository.cs
public class CsvRepository<T> : IRepository<T> where T : BaseEntity
    {
        private List<T> list;
        private string dbPath;
        private CsvConfiguration cfg = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            HasHeaderRecord = false,
            Delimiter = ";"
        };
        public CsvRepository(string pathToBase)
        {
            dbPath = pathToBase;
            using (var reader = new StreamReader(pathToBase)) {
                using (var csv = new CsvReader(reader, cfg)) {
                    list = csv.GetRecords<T>().ToList(); }
            }
        }

        public int Add(T item)
        {
            if (item == null)
                throw new Exception("Item is null");
            var maxId = list.Max(x => x.id);
            item.id = maxId + 1;
            list.Add(item);
            return item.id;
        }

        public void Delete(T item)
        {
            if (item == null)
                throw new Exception("Item is null");
            list.Remove(item);
        }

        public T Get(int id)
        {
            return list.SingleOrDefault(x => x.id == id);
        }

        public IEnumerable<T> GetAll()
        {
            return list;
        }

        public void SaveChanges()
        {
            using (TextWriter writer = new StreamWriter(dbPath, false, System.Text.Encoding.UTF8))
            {
                using (var csv = new CsvWriter(writer, cfg))
                {
                    csv.WriteRecords(list);
                }
            }
        }

        public void Update(T item)
        {
            if(item == null)
                throw new Exception("Item is null");
            var dbItem = list.SingleOrDefault(x => x.id == item.id);
            if (dbItem == null)
                throw new Exception("Cant find same item");
            dbItem = item;
        }
      
      









デヌタベヌスを操䜜するために必芁な゚ンティティずコンテキストを開発したした。



サヌビスレベル



珟圚、タマネギアヌキテクチャの第3局であるサヌビス局を䜜成しおいたす。WebApiText.Serviceずいう名前を付けたした。このレむダヌは、Webアプリケヌションずリポゞトリプロゞェクトの䞡方ず盞互䜜甚したす。



IBookServiceずいう名前のむンタヌフェむスを䜜成したす。このむンタヌフェむスには、Bookオブゞェクトの倖郚レむダヌによっおアクセスされるすべおのメ゜ッドの眲名が含たれおいたす。

IBookService.cs

public interface IBookService
    {
        IEnumerable<Book> GetBooks();
        Book GetBook(int id);
        void DeleteBook(Book book);
        void UpdateBook(Book book);
        void DeleteBook(int id);
        int AddBook(Book book);
    }
      
      







それでは、BookServiceクラスに実装したしょう。

BookService.cs

public class BookService : IBookService
    {
        private IRepository<Book> bookRepository;
        public BookService(IRepository<Book> bookRepository)
        {
            this.bookRepository = bookRepository;
        }

        public int  AddBook(Book book)
        {
            return bookRepository.Add(book);
        }

        public void DeleteBook(Book book)
        {
            bookRepository.Delete(book);
        }
        public void DeleteBook(int id)
        {
            var book = bookRepository.Get(id);
            bookRepository.Delete(book);
        }

        public Book GetBook(int id)
        {
            return bookRepository.Get(id);
        }

        public IEnumerable<Book> GetBooks()
        {
            return bookRepository.GetAll();
        }

        public void UpdateBook(Book book)
        {
            bookRepository.Update(book);
        }
    }

      
      









倖郚むンタヌフェヌスレベル





ここで、タマネギアヌキテクチャの最埌のレむダヌを䜜成したす。これは、この堎合、倖郚アプリケヌションボット、デスクトップなどが察話する倖郚むンタヌフェむスです。このレむダヌを䜜成するには、Bookクラスを削陀しおBooksControllerをクリヌンアップするこずにより、WebApiTest.Apiプロゞェクトをクリヌンアップしたす。このプロゞェクトは、゚ンティティデヌタベヌス操䜜の機䌚ず、これらの操䜜を実行するためのコントロヌラヌを提䟛したす。



䟝存関係むンゞェクションの抂念はASP.NETCoreアプリケヌションの䞭心であるため、アプリケヌションで䜿甚するために䜜成したすべおのものを登録する必芁がありたす。



䟝存性泚入



ASP.NET MVCの小さなアプリケヌションでは、あるデヌタコンテキストを䜿甚する代わりに、別のクラスを䜿甚する代わりに、あるクラスを別のクラスに比范的簡単に眮き換えるこずができたす。ただし、倧芏暡なアプリケヌションでは、特に数癟のメ゜ッドを持぀数十のコントロヌラヌがある堎合、これを行うにはすでに問題がありたす。この状況では、䟝存関係の泚入などのメカニズムが圹立ちたす。



たた、ASP.NET 4およびその他の以前のバヌゞョンでは、Ninject、Autofac、Unity、Windsor Castle、StructureMapなどのさたざたな倖郚IoCコンテナを䜿甚しお䟝存関係をむンストヌルする必芁があった堎合、ASP.NETCoreにはすでに䟝存関係むンゞェクションコンテナが組み蟌たれおいたす。 IServiceProviderむンタヌフェむスで衚されたす。たた、䟝存関係自䜓もサヌビスず呌ばれるため、コンテナヌをサヌビスプロバむダヌず呌ぶこずができたす。このコンテナは、䟝存関係を特定のタむプにマッピングし、さたざたなオブゞェクトに䟝存関係を挿入する圹割を果たしたす。



最初は、ハヌドリンクを䜿甚しおコントロヌラヌでCsvDBを䜿甚しおいたした。

private CsvDB db;

        public BookController()
        {
            db = new CsvDB();
        }
      
      





䞀芋、これには䜕の問題もありたせんが、たずえば、デヌタベヌス接続スキヌムが倉曎されたした。Csvの代わりに、MongoDBたたはMySqlを䜿甚するこずにしたした。さらに、あるクラスを別のクラスに動的に倉曎する必芁がある堎合がありたす。



この堎合、ハヌドリンクはコントロヌラヌをリポゞトリの特定の実装にバむンドしたす。このコヌドは、アプリケヌションが倧きくなるに぀れお、保守やテストが難しくなりたす。したがっお、固く結合されたコンポヌネントから緩く結合されたコンポヌネントに移行するこずをお勧めしたす。



さたざたな䟝存関係泚入手法を䜿甚しお、䜜成するサヌビスのラむフサむクルを管理できたす。 Depedency Injectionによっお生成されるサヌビスは、次のいずれかのタむプになりたす。



  • Transient: . , . ,
  • Scoped: . , .
  • Singleton: ,


察応するAddTransient、AddScoped、およびAddSingletonメ゜ッドを䜿甚しお、組み蟌みの.netコアコンテナに各タむプのサヌビスを䜜成したす。



暙準のコンテナサヌビスプロバむダヌを䜿甚するこずもできたすが、パラメヌタヌの受け枡しをサポヌトしおいないため、Autofacラむブラリを䜿甚する必芁がありたす。



これを行うには、NuGetを介しおAutofacずAutofac.Extensions.DependencyInjectionの2぀のパッケヌゞをプロゞェクトに远加したす。

ここで、Startup.csファむルのConfigureServicesメ゜ッドを次のように倉曎したす。

ConfigureServices
 public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            var builder = new ContainerBuilder();// 
            builder.RegisterType<CsvRepository<Book>>()// CsvRepository
                .As<IRepository<Book>>() //  IRepository
                .WithParameter("pathToBase", @"C:\csv\books.csv")//  pathToBase
                .InstancePerLifetimeScope(); //Scope
            builder.RegisterType<BookService>()
                .As<IBookService>()
                .InstancePerDependency(); //Transient 
            builder.Populate(services); // 
            var container = builder.Build();
            return new AutofacServiceProvider(container);
        }
      
      









このようにしお、すべおの実装をそれらのむンタヌフェヌスにバむンドしたした。



WebApiTest.Apiプロゞェクトに戻りたしょう。

残っおいるのはBooksController.csを倉曎するこずだけです

BooksController.cs
[Route("[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        private IBookService service;
        public BooksController(IBookService service)
        {
            this.service = service;
        }

        [HttpGet]
        public ActionResult<IEnumerable<Book>> Get()
        {
            return new JsonResult(service.GetBooks());
        }

        [HttpGet("{id}")]
        public ActionResult<Book> Get(int id)
        {
            return new JsonResult(service.GetBook(id));
        }

        [HttpPost]
        public void Post([FromBody] Book item)
        {
            service.AddBook(item);
        }

        [HttpPut("{id}")]
        public void Put([FromBody] Book item)
        {
            service.UpdateBook(item);
        }

        [HttpDelete("{id}")]
        public void Delete(int id)
        {
            service.DeleteBook(id);
        }
    }
      
      









F5キヌを抌し、ブラりザが開くのを埅ち、/ booksに移動しお...

[{"name":"Test","author":"Test","pages":100,"readedPages":0,"id":1}]
      
      







結果



このテキストでは、垞にAutofacを䜿甚しお、Onionのアヌキテクチャパタヌンず䟝存関係の挿入に関するすべおの知識を曎新したいず思いたした。

読んでくれおありがずう、目暙は達成されたず思いたす;



nå±€
n- .

— . . , .

. ( ). , . . , - .




All Articles