日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Dotnet洋葱架构实践

發布時間:2023/12/4 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Dotnet洋葱架构实践 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一個很清晰的架構實踐,同時刨刨MySQL的坑。

?

一、洋蔥架構簡介

洋蔥架構出來的其實有一點年頭了。大約在2017年下半年,就有相關的說法了。不過,大量的文章在于理論性的討論,而我們今天會用一個項目來完成這個架構。

?

洋蔥架構,有時候也被叫做整潔架構,它本身是為高質量的軟件而存在的。

相對其它架構而言,洋蔥架構具有更好的可測試性、實用性和穩定性,并且足夠靈活,完全適應項目未來可能的成長和進化??梢赃@么說,洋蔥架構完美解決了三層或N層架構所面臨的困難和問題。

牛吹完了,下面來看張圖:

這張圖,充分解釋了它為什么叫洋蔥架構。

不過,這不是重點。這個架構最重要的是里面的代碼依賴原則:從外向內,并且只有這一個方向。處于內環的代碼,不應該知道外環的任何東西

?

從上面圖也可以看到,洋蔥架構,也使用層的概念。不過,它不同于我們習慣的三層或N層。我們來看看每個層的情況:

  • 數據層(Domain Layer)

存在于架構的中心部分,由所有業務數據的實體組成。大多數情況下,就是我們的數據模型。后面的實踐代碼中,我是用EF(Entity Framework)來操作的數據庫。

  • 存儲層(Repository Layer)

存儲層在架構中充當服務層和數據模型之間的紐帶,并且在這一層將保持所有數據庫操作和應用數據的上下文。通常的做法是接口,用接口來描述數據訪問所涉及的讀寫操作。

  • 服務層(Services Layer)

服務層用于實現存儲層和項目之間的通信,同時,還可以保存實體的業務邏輯。在這一層,服務接口與實現分離,以實現解耦和焦點分離。

  • 用戶界面層(UI Layer)

這個不解釋了。項目最終對外的一層。注意,這兒可能是網站,也可能是API。不需要糾結有沒有實際的界面。咱們的實踐代碼中,我用的是API。

? ?二、實踐

好,現在直接進入代碼。

1. 創建工程

這個不解釋了,都是套路:

%?dotnet?new?webapi?-o?demo?-f?netcoreapp3.1

我這個工程用的是Dotnet Core 3.1。框架不重要,基本上哪個版本都可以用。

?

下面設置Swagger

這個是我的習慣,而且這個項目是個WebApi,裝個Swagger方便。

%?dotnet?add?package?swashbuckle.aspnetcore

Swagger的設置不是本文的重點,略過。需要的同學可以去看源代碼。

?

下面,我們在工程中建三個目錄:

  • DomainLayer

  • RepositoryLayer

  • ServicesLayer

這三個目錄對應上面的三個層。UI在這個項目里其實就是控制器Controller,已經存在了。

建這三個目錄的目的,是為了放置三個層的代碼。后面編碼的時候,你會看到這三個層之間的關系。另外,這三個層在實際應用時,可以獨立為三個類庫,這樣會更清晰。

?

前邊說了,我會用EF操作數據庫。所以,這兒還需要引入三個庫:

%?dotnet?add?package?Microsoft.EntityFrameworkCore %?dotnet?add?package?Microsoft.EntityFrameworkCore.Relational %?dotnet?add?package?Pomelo.EntityFrameworkCore.MySql

注意,微軟的EF框架沒有提供MySQL的接入,所以引用了一個三方的庫。

?

至此,項目的準備工作完成。

2. 實現數據層

在DomainLayer目錄里,建一個Models目錄。在Models目錄下,建兩個類:

BaseEntity.cs

public?class?BaseEntity {public?int?Id?{?get;?set;?}public?DateTime?CreatedDate?{?get;?set;?}public?DateTime?ModifiedDate?{?get;?set;?}public?bool?IsActive?{?get;?set;?} }

Customer.cs

public?class?Customer?:?BaseEntity {public?string?CustomerName?{?get;?set;?}public?string?PurchasesProduct?{?get;?set;?}public?string?PaymentType?{?get;?set;?} }

兩個類,Customer派生自BaseEntity。沒什么特殊的含義,也是一個習慣。而且,后面到存儲層寫著方便。

?

后面,我們會用到Customer和BaseEntity實體類創建的數據表。為了讓大家看的明白,我在這兒建一個目錄EntityMapper,在目錄里寫個表結構映射。

CustomerMap.cs

public?class?CustomerMap?:?IEntityTypeConfiguration<Customer> {public?void?Configure(EntityTypeBuilder<Customer>?builder){builder.HasKey(x?=>?x.Id).HasName("pk_customerid");builder.Property(x?=>?x.Id).ValueGeneratedOnAdd().HasColumnName("id").HasColumnType("INT");builder.Property(x?=>?x.CustomerName).HasColumnName("customer_name").HasColumnType("NVARCHAR(100)");builder.Property(x?=>?x.PurchasesProduct).HasColumnName("purchased_product").HasColumnType("NVARCHAR(100)").IsRequired();builder.Property(x?=>?x.PaymentType).HasColumnName("payment_type").HasColumnType("NVARCHAR(50)").IsRequired();builder.Property(x?=>?x.CreatedDate).HasColumnName("created_date").HasColumnType("datetime");builder.Property(x?=>?x.ModifiedDate).HasColumnName("modified_date").HasColumnType("datetime");builder.Property(x?=>?x.IsActive).HasColumnName("is_active").HasColumnType("bit");} }

或者也可以自己創建一個表ef.Customer:

CREATE?TABLE?`Customer`?(`id`?int?NOT?NULL?AUTO_INCREMENT,`created_date`?datetime?DEFAULT?NULL,`customer_name`?varchar(255)?CHARACTER?SET?utf8mb4?COLLATE?utf8mb4_0900_ai_ci?DEFAULT?NULL,`is_active`?bit(1)?DEFAULT?NULL,`modified_date`?datetime?DEFAULT?NULL,`payment_type`?varchar(50)?DEFAULT?NULL,`purchased_product`?varchar(100)?DEFAULT?NULL,PRIMARY?KEY?(`id`)?USING?BTREE )

3. 實現存儲層

這個層,主要用來操作數據庫。

先在Startup.cs中配置數據庫引用:

public?class?Startup {public?void?ConfigureServices(IServiceCollection?services){services.AddDbContextPool<ApplicationDbContext>(options?=>?options.UseMySql("server=192.168.0.241;user=root;password=xxxxxx;database=ef",new?MySqlServerVersion(new?Version(8,?0,?21)),mysqlOptions?=>{mysqlOptions.CharSetBehavior(CharSetBehavior.NeverAppend);}));} }

這兒偷個懶,連接串就直接寫代碼里了。正式做項目時,最好寫在配置文件中。

?

在RepositoryLayer目錄中建一個DataContext,里面用來放置相關數據庫會話,和操作的實例:

ApplicationDbContext.cs

public?partial?class?ApplicationDbContext?:?DbContext {public?ApplicationDbContext(DbContextOptions?options)?:?base(options){}protected?override?void?OnModelCreating(ModelBuilder?modelBuilder){modelBuilder.ApplyConfiguration(new?CustomerMap());base.OnModelCreating(modelBuilder);} }

再建個目錄RespositoryPattern,用來存放數據庫操作的類。按照注入的原則,會是兩個文件,一個接口定義,一個實現類:

IRepository.cs

public?interface?IRepository<T>?where?T?:?BaseEntity {IEnumerable<T>?GetAll();T?Get(int?Id);void?Insert(T?entity);void?Update(T?entity);void?Delete(T?entity);void?Remove(T?entity);void?SaveChanges(); }

Repository.cs

public?class?Repository<T>?:?IRepository<T>?where?T?:?BaseEntity {private?readonly?ApplicationDbContext?_applicationDbContext;private?DbSet<T>?entities;public?Repository(ApplicationDbContext?applicationDbContext){_applicationDbContext?=?applicationDbContext;entities?=?_applicationDbContext.Set<T>();}public?void?Delete(T?entity){if?(entity?==?null){throw?new?ArgumentNullException("entity");}entities.Remove(entity);_applicationDbContext.SaveChanges();}public?T?Get(int?Id){return?entities.SingleOrDefault(c?=>?c.Id?==?Id);}public?IEnumerable<T>?GetAll(){return?entities.AsEnumerable();}public?void?Insert(T?entity){if?(entity?==?null){throw?new?ArgumentNullException("entity");}entities.Add(entity);_applicationDbContext.SaveChanges();}public?void?Remove(T?entity){if?(entity?==?null){throw?new?ArgumentNullException("entity");}entities.Remove(entity);}public?void?SaveChanges(){_applicationDbContext.SaveChanges();}public?void?Update(T?entity){if?(entity?==?null){throw?new?ArgumentNullException("entity");}entities.Update(entity);_applicationDbContext.SaveChanges();} }

4. 實現服務層

服務層用來實現核心的業務邏輯。同樣先建一個目錄CustomerService,方便注入,也是一個接口一個類:

ICustomerService.cs

public?interface?ICustomerService {IEnumerable<Customer>?GetAllCustomers();Customer?GetCustomer(int?id);void?InsertCustomer(Customer?customer);void?UpdateCustomer(Customer?customer);void?DeleteCustomer(int?id); }

CustomerService.cs

public?class?CustomerService?:?ICustomerService {private?IRepository<Customer>?_repository;public?CustomerService(IRepository<Customer>?repository){_repository?=?repository;}public?IEnumerable<Customer>?GetAllCustomers(){return?_repository.GetAll();}public?Customer?GetCustomer(int?id){return?_repository.Get(id);}public?void?InsertCustomer(Customer?customer){_repository.Insert(customer);}public?void?UpdateCustomer(Customer?customer){_repository.Update(customer);}public?void?DeleteCustomer(int?id){Customer?customer?=?GetCustomer(id);_repository.Remove(customer);_repository.SaveChanges();} }

4. 注入

這兒就是套路了,不解釋。

public?void?ConfigureServices(IServiceCollection?services) {services.AddScoped(typeof(IRepository<>),?typeof(Repository<>));services.AddTransient<ICustomerService,?CustomerService>(); }

5. 實現控制器

重要的三層都已經實現。下面做個演示用的控制器:

CustomerController.cs

[ApiController] [Route("[controller]")] public?class?CustomerController?:?ControllerBase {private?readonly?ICustomerService?_customerService;public?CustomerController(ICustomerService?customerService){_customerService?=?customerService;}[HttpGet(nameof(GetCustomer))]public?IActionResult?GetCustomer(int?id){var?result?=?_customerService.GetCustomer(id);if?(result?!=?null){return?Ok(result);}return?BadRequest("No?records?found");}[HttpGet(nameof(GetAllCustomer))]public?IActionResult?GetAllCustomer(){var?result?=?_customerService.GetAllCustomers();if?(result?!=?null){return?Ok(result);}return?BadRequest("No?records?found");}[HttpPost(nameof(InsertCustomer))]public?IActionResult?InsertCustomer(Customer?customer){_customerService.InsertCustomer(customer);return?Ok("Data?inserted");}[HttpPut(nameof(UpdateCustomer))]public?IActionResult?UpdateCustomer(Customer?customer){_customerService.UpdateCustomer(customer);return?Ok("Updation?done");}[HttpDelete(nameof(DeleteCustomer))]public?IActionResult?DeleteCustomer(int?Id){_customerService.DeleteCustomer(Id);return?Ok("Data?Deleted");} }

?

代碼部分全部完成。編譯運行~~~

三、總結

通過上面的代碼可以看到:

  • 洋蔥架構各層間通過接口互相關聯,數據引入是在運行時進行的

  • 應用以區域模型為基礎

  • 所有的外部依賴,如數據集準入和管理調,都是在外部處理

  • 適應性強,設計也方便

總之,從應用來說,洋蔥架構算是個很優秀的架構。以我的經驗,在多個共同開發的項目中,具有比較大的優勢。

?

本文的相關代碼,在https://github.com/humornif/Demo-Code/tree/master/0045/demo

喜歡就來個三連,讓更多人因你而受益

總結

以上是生活随笔為你收集整理的Dotnet洋葱架构实践的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产女人18毛片水真多 | 黄视频网站在线看 | 人人艹人人爱 | 88av视频| 怡红院av久久久久久久 | 天天干影院 | 亚洲高清久久 | 精品国偷自产一区二区三区 | 1024欧美 | 国产精品夜夜夜爽张柏芝 | 亚洲国产精品麻豆 | 亚洲影视一区二区三区 | 窝窝午夜看片 | 少妇激情一区二区三区 | 欧美日韩中文国产一区发布 | 无码国产精品一区二区色情男同 | 懂色av| 国产三级视频在线播放 | 午夜免费福利影院 | 91天天看 | 日p视频在线观看 | 国产精品一区二区在线观看 | 办公室摸腿吻胸激情视频 | 欧美片免费网站 | 亚洲不卡在线播放 | 99re6在线观看| 日本一区二区三区网站 | 国产aⅴ爽av久久久久成人 | 色吊丝一区二区 | 国产3区| 人妻在客厅被c的呻吟 | 一级特黄aa大片欧美 | 欧美大片免费观看网址 | 色哟哟网站入口 | 日韩高清专区 | 激情福利 | 在线观看二区 | 久久艹艹| 一区二区不卡免费视频 | 亚洲午夜精品久久久久久浪潮 | 免费在线观看小视频 | 日本va欧美va国产激情 | a在线观看 | 色偷偷在线观看 | 你懂的欧美 | 日本久草视频 | 久草在 | 亚洲精品在线观看网站 | 久久黄色影院 | 免费在线不卡av | 一起草视频在线播放 | 午夜激情综合 | 99精品在线 | 日韩在线一区二区三区四区 | 无套内谢大学处破女www小说 | 精品国产免费人成在线观看 | 噼里啪啦高清 | 一区二区三区啪啪啪 | 亚洲少妇30p | 18视频在线观看男男 | se综合| 久久久久久久999 | 黄色片网站视频 | 欧洲精品在线播放 | 亚洲在线观看一区二区 | 欧美一区二区高清视频 | 国产探花一区 | 亚洲精品国产精品乱码在线观看 | 欧美色图视频在线 | 不卡的毛片 | 久久久久国产精品区片区无码 | 三上悠亚久久精品 | 国产丰满麻豆 | 制服下的诱惑暮生 | 国产高清露脸 | 午夜寂寞院| 龚玥菲一级淫片 | 在线观看va| 超碰人人艹 | 热播之家 | 成人在线超碰 | 91日本在线 | 成人a级网站 | 日韩黄色短片 | 国产精品久久久久久亚洲伦 | 日韩网站在线 | 一级片黄色片 | 婷婷丁香一区二区三区 | 欧美一区成人 | 日韩中文字幕2019 | 色激情五月 | 黑帮大佬和我的三百六十五天 | 91亚洲一区二区三区 | jizz一区 | 在线h片 | 亚洲av少妇一区二区在线观看 | 国产,日韩,欧美 | 豆豆色成人网 | 在线观看中文字幕一区 |