日韩性视频-久久久蜜桃-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洋葱架构实践的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 国产精品久久久久久久久久久久久久久久久 | 校霸被c到爽夹震蛋上课高潮 | aaaaav| 亲嘴扒胸摸屁股激烈网站 | 激情小说一区 | 少妇精品| 男女互操| 在线观看第一页 | 精品无码国产一区二区三区51安 | 精品视频一区二区 | 亚洲乱码一区二区 | 先锋资源在线视频 | 亚洲欧美乱综合图片区小说区 | 欧美作爱视频 | 国产高中女学生第一次 | 成年人视屏 | 日韩中文字幕网 | www.色多多| 伊人春色av| 色臀av| 婷婷一级片 | 国产综合久久久久 | 人成在线| 波多野结衣 久久 | 综合色导航 | 久久看片网 | 高h捆绑拘束调教小说 | 日韩精品电影一区二区 | 狠狠做深爱婷婷综合一区 | 台湾性生生活1 | 国产乱淫av麻豆国产 | 国产视频日韩 | 四虎影院在线免费播放 | 亚洲成网| 免费福利视频网站 | 韩国一区二区在线观看 | 国内av自拍 | 国产精品免费久久久 | 亚洲第一成人av | 天天碰免费视频 | 国产视频在线免费观看 | 精品九九| 97超碰成人 | 国产精品2020 | 国产又猛又黄又爽 | 久久av免费看 | 久久国产高清 | 青青青手机在线视频 | 日韩中文欧美 | 免费av网页 | 国内精品视频在线 | 国产在线一二区 | 求av网址 | 一级黄片毛片 | 思思99re| av网站国产| 国产在线播放一区二区三区 | 69国产 | 国产老熟女一区二区三区 | 黑人巨大猛烈捣出白浆 | 影音先锋黄色网址 | 欧美中文字幕 | 国产女同视频 | 波多野结衣中文字幕在线 | 销魂奶水汁系列小说 | 午夜污污 | 国产丰满农村老妇女乱 | 国产av一区二区三区 | 美女看片 | 亚洲狠狠婷婷综合久久久久图片 | 欧美xxxx日本和非洲 | 日韩免费在线观看视频 | 狠狠躁日日躁夜夜躁2022麻豆 | 一女被多男玩喷潮视频 | 免费av一区二区三区 | 999精品免费视频 | 精品无码人妻一区二区免费蜜桃 | 国产精品va无码一区二区 | 丁香六月婷婷 | 黄色片99| 日韩欧美国产高清91 | 亚洲欧美第一页 | 国产精品一区av | 中文av网站 | 中文字幕五码 | 国产精品51麻豆cm传媒 | 污片网站在线观看 | 国产123区在线观看 91国产一区二区 | yy6080久久| 91一区二区三区 | 中文字幕在线视频不卡 | 精品中文视频 | 经典三级在线视频 | 内射无码专区久久亚洲 | 欧美30p | 日本久久久久 | 18视频在线观看娇喘 | 国产精品久久久久不卡 | 影音先锋黄色资源 |