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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

使用 ASP.NET Core, Entity Framework Core 和 ABP 创建N层Web应用 第二篇

發布時間:2023/12/4 asp.net 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用 ASP.NET Core, Entity Framework Core 和 ABP 创建N层Web应用 第二篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

介紹

這是“使用 ASP.NET Core ,Entity Framework Core 和 ASP.NET Boilerplate 創建N層 Web 應用”系列文章的第二篇。以下可以看其他篇目:

  • 使用 ASP.NET Core ,Entity Framework Core 和 ASP.NET Boilerplate 創建N層 Web 應用 ?第一篇 (翻譯版本鏈接)

應用開發

創建 Person 實體

我們將任務分配給具體的人,所以添加一個責任人的概念。我們定義一個簡單的 Person 實體。

代碼如下

[Table("AppPersons")]

public class Person : AuditedEntity<Guid>

{

? ? public const int MaxNameLength = 32;


? ? [Required]

? ? [MaxLength(MaxNameLength)]

? ? public string Name { get; set; }


? ? public Person()

? ? {

? ? ? ? ? ??

? ? }


? ? public Person(string name)

? ? {

? ? ? ? Name = name;

? ? }

}

這一次,我們作為示范,將 Id (主鍵)設置為 Guid 類型。同時,這次不從 base Entity 繼承,而是從 AuditedEntity 繼承 (該類定義了多個常用屬性 創建時間?CreationTime, 創建者用戶Id CreaterUserId, 最后修改時間 LastModificationTime 和最后修改人Id LastModifierUserId?)

?

關聯 Person 到 Task 實體

我們同時將 責任人 AssignedPerson 屬性添加到 任務 Task 實體中(如下代碼只粘貼修改的部分)

代碼如下

[Table("AppTasks")]

public class Task : Entity, IHasCreationTime

{

? ? //...


? ? [ForeignKey(nameof(AssignedPersonId))]

? ? public Person AssignedPerson { get; set; }

? ? public Guid? AssignedPersonId { get; set; }


? ? public Task(string title, string description = null, Guid? assignedPersonId = null)

? ? ? ? : this()

? ? {

? ? ? ? Title = title;

? ? ? ? Description = description;

? ? ? ? AssignedPersonId = assignedPersonId;

? ? }

}

責任人 AssignedPerson 是可選的。所以,任務可以指派給責任人或者不指派

?

添加 Person 到 數據庫上下文 DbContext

最后,我們添加新的責任人 Person 實體到 DbContext 類中:

代碼如下

public class SimpleTaskAppDbContext : AbpDbContext

{

? ? public DbSet<Person> People { get; set; }

? ??

? ? //...

}

添加 Person 實體的新遷移文件

現在,我們在 源包管理控制臺 Package Manager Console 中執行遷移命令,如圖所示

該命令將會在項目里創建新的數據遷移類。

代碼如下

public partial class Added_Person : Migration

{

? ? protected override void Up(MigrationBuilder migrationBuilder)

? ? {

? ? ? ? migrationBuilder.CreateTable(

? ? ? ? ? ? name: "AppPersons",

? ? ? ? ? ? columns: table => new

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Id = table.Column<Guid>(nullable: false),

? ? ? ? ? ? ? ? CreationTime = table.Column<DateTime>(nullable: false),

? ? ? ? ? ? ? ? CreatorUserId = table.Column<long>(nullable: true),

? ? ? ? ? ? ? ? LastModificationTime = table.Column<DateTime>(nullable: true),

? ? ? ? ? ? ? ? LastModifierUserId = table.Column<long>(nullable: true),

? ? ? ? ? ? ? ? Name = table.Column<string>(maxLength: 32, nullable: false)

? ? ? ? ? ? },

? ? ? ? ? ? constraints: table =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? table.PrimaryKey("PK_AppPersons", x => x.Id);

? ? ? ? ? ? });


? ? ? ? migrationBuilder.AddColumn<Guid>(

? ? ? ? ? ? name: "AssignedPersonId",

? ? ? ? ? ? table: "AppTasks",

? ? ? ? ? ? nullable: true);


? ? ? ? migrationBuilder.CreateIndex(

? ? ? ? ? ? name: "IX_AppTasks_AssignedPersonId",

? ? ? ? ? ? table: "AppTasks",

? ? ? ? ? ? column: "AssignedPersonId");


? ? ? ? migrationBuilder.AddForeignKey(

? ? ? ? ? ? name: "FK_AppTasks_AppPersons_AssignedPersonId",

? ? ? ? ? ? table: "AppTasks",

? ? ? ? ? ? column: "AssignedPersonId",

? ? ? ? ? ? principalTable: "AppPersons",

? ? ? ? ? ? principalColumn: "Id",

? ? ? ? ? ? onDelete: ReferentialAction.SetNull);

? ? }


? ? //...

}

該類為自動生成的,我們只是將 ReferentialAction.Restrict 修改為 ReferentialAction.SetNull 。它的作用是:當我們刪除一個責任人的時候,分配給這個人的任務會變成為分配。在這個 demo 里,這并不重要。我們只是想告訴你,如果有必要的話,遷移類的代碼是可以修改的。實際上,我們總是應該在執行到數據庫之前,重新閱讀生成的代碼。

之后,我們可以對我們的數據庫執行遷移了。如下圖:(更多遷移相關信息請參照 ?entity framework documentation?)

當我們打開數據庫的時候,我們可以看到表和字段都已經創建完畢了,我們可以添加一些測試數據。如下圖:

我們添加一個責任人并分配第一個任務給他。如下圖:

?

返回任務列表中的責任人 Person

我們將修改 TaskAppService ,使之可以返回責任人信息。首先,我們在 TaskListDto 中添加2個屬性。

代碼如下 (只顯示有變動的代碼,如需看完整代碼請參考第一篇,下同)

[AutoMapFrom(typeof(Task))]

public class TaskListDto : EntityDto, IHasCreationTime

{

? ? //...


? ? public Guid? AssignedPersonId { get; set; }


? ? public string AssignedPersonName { get; set; }

}

同時將 Task.AssignedPerson 屬性添加到查詢里,僅添加 Include 行即可

代碼如下

public class TaskAppService : SimpleTaskAppAppServiceBase, ITaskAppService

{

? ? //...


? ? public async Task<ListResultDto<TaskListDto>> GetAll(GetAllTasksInput input)

? ? {

? ? ? ? var tasks = await _taskRepository

? ? ? ? ? ? .GetAll()

? ? ? ? ? ? .Include(t => t.AssignedPerson)

? ? ? ? ? ? .WhereIf(input.State.HasValue, t => t.State == input.State.Value)

? ? ? ? ? ? .OrderByDescending(t => t.CreationTime)

? ? ? ? ? ? .ToListAsync();


? ? ? ? return new ListResultDto<TaskListDto>(

? ? ? ? ? ? ObjectMapper.Map<List<TaskListDto>>(tasks)

? ? ? ? );

? ? }

}

這樣, GetAll 方法會返回任務及相關的責任人信息。由于我們使用了 AutoMapper , 新的屬性也會自動添加到 DTO 里。

?

單元測試責任人 Person


在這里,我們修改單元測試,(對測試不感興趣者可直接跳過)看看獲取任務列表時是否能獲取到責任人。首先,我們修改 TestDataBuilder 類里的初始化測試數據,分配任務給責任人。

代碼如下

public class TestDataBuilder

{

? ? //...


? ? public void Build()

? ? {

? ? ? ? var neo = new Person("Neo");

? ? ? ? _context.People.Add(neo);

? ? ? ? _context.SaveChanges();


? ? ? ? _context.Tasks.AddRange(

? ? ? ? ? ? new Task("Follow the white rabbit", "Follow the white rabbit in order to know the reality.", neo.Id),

? ? ? ? ? ? new Task("Clean your room") { State = TaskState.Completed }

? ? ? ? ? ? );

? ? }

}

然后我們修改 TaskAppService_Tests.Should_Get_All_Tasks() 方法,檢查是否有一個任務已經指派了責任人(請看代碼最后一行)

代碼如下

[Fact]

public async System.Threading.Tasks.Task Should_Get_All_Tasks()

{

? ? //Act

? ? var output = await _taskAppService.GetAll(new GetAllTasksInput());


? ? //Assert

? ? output.Items.Count.ShouldBe(2);

? ? output.Items.Count(t => t.AssignedPersonName != null).ShouldBe(1);

}

友情提示:擴張方法 Count 需要使用 using System.Linq 語句。

?

在任務列表頁展示責任人的名字

最后,我們修改 Task\Index.cshtml 來展示 責任人的名字 AssignedPersonName 。

代碼如下

@foreach (var task in Model.Tasks)

{

? ? <li class="list-group-item">

? ? ? ? <span class="pull-right label label-lg @Model.GetTaskLabel(task)">@L($"TaskState_{task.State}")</span>

? ? ? ? <h4 class="list-group-item-heading">@task.Title</h4>

? ? ? ? <div class="list-group-item-text">

? ? ? ? ? ? @task.CreationTime.ToString("yyyy-MM-dd HH:mm:ss") | @(task.AssignedPersonName ?? L("Unassigned"))

? ? ? ? </div>

? ? </li>

}

啟動程序,我們可以看到任務列表入下圖:

?

任務創建的新應用服務方法

現在我們可以展示所有的任務,但是我們卻還沒有一個任務創建頁面。首先,在 ITaskAppService 接口里添加一個 Create 方法。

代碼如下

public interface ITaskAppService : IApplicationService

{

? ? //...


? ? System.Threading.Tasks.Task Create(CreateTaskInput input);

}

然后在 TaskAppService 類里實現它

代碼如下

public class TaskAppService : SimpleTaskAppAppServiceBase, ITaskAppService

{

? ? private readonly IRepository<Task> _taskRepository;


? ? public TaskAppService(IRepository<Task> taskRepository)

? ? {

? ? ? ? _taskRepository = taskRepository;

? ? }


? ? //...


? ? public async System.Threading.Tasks.Task Create(CreateTaskInput input)

? ? {

? ? ? ? var task = ObjectMapper.Map<Task>(input);

? ? ? ? await _taskRepository.InsertAsync(task);

? ? }

}

Create 方法會自動映射輸入參數 input 到task 實體,之后我們使用倉儲 repository 來將任務實體插入數據庫中。讓我們來看看輸入參數 input 的 CreateTaskInput DTO 。

代碼如下

using System;

using System.ComponentModel.DataAnnotations;

using Abp.AutoMapper;


namespace Acme.SimpleTaskApp.Tasks.Dtos

{

? ? [AutoMapTo(typeof(Task))]

? ? public class CreateTaskInput

? ? {

? ? ? ? [Required]

? ? ? ? [MaxLength(Task.MaxTitleLength)]

? ? ? ? public string Title { get; set; }


? ? ? ? [MaxLength(Task.MaxDescriptionLength)]

? ? ? ? public string Description { get; set; }


? ? ? ? public Guid? AssignedPersonId { get; set; }

? ? }

}

我們將DTO配置為映射到任務 Task 實體(使用 AutoMap 特性),同時添加數據驗證 validation?。我們使用任務 Task 實體的常量來同步設置最大字串長度。?

?

測試任務創建服務

我們添加 TaskAppService_Tests 類的集成測試來測試 Create 方法:(如果對測試不感興趣者可以跳過這個部分

代碼如下

using Acme.SimpleTaskApp.Tasks;

using Acme.SimpleTaskApp.Tasks.Dtos;

using Shouldly;

using Xunit;

using System.Linq;

using Abp.Runtime.Validation;


namespace Acme.SimpleTaskApp.Tests.Tasks

{

? ? public class TaskAppService_Tests : SimpleTaskAppTestBase

? ? {

? ? ? ? private readonly ITaskAppService _taskAppService;


? ? ? ? public TaskAppService_Tests()

? ? ? ? {

? ? ? ? ? ? _taskAppService = Resolve<ITaskAppService>();

? ? ? ? }


? ? ? ? //...


? ? ? ? [Fact]

? ? ? ? public async System.Threading.Tasks.Task Should_Create_New_Task_With_Title()

? ? ? ? {

? ? ? ? ? ? await _taskAppService.Create(new CreateTaskInput

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Title = "Newly created task #1"

? ? ? ? ? ? });


? ? ? ? ? ? UsingDbContext(context =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? var task1 = context.Tasks.FirstOrDefault(t => t.Title == "Newly created task #1");

? ? ? ? ? ? ? ? task1.ShouldNotBeNull();

? ? ? ? ? ? });

? ? ? ? }


? ? ? ? [Fact]

? ? ? ? public async System.Threading.Tasks.Task Should_Create_New_Task_With_Title_And_Assigned_Person()

? ? ? ? {

? ? ? ? ? ? var neo = UsingDbContext(context => context.People.Single(p => p.Name == "Neo"));


? ? ? ? ? ? await _taskAppService.Create(new CreateTaskInput

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Title = "Newly created task #1",

? ? ? ? ? ? ? ? AssignedPersonId = neo.Id

? ? ? ? ? ? });


? ? ? ? ? ? UsingDbContext(context =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? var task1 = context.Tasks.FirstOrDefault(t => t.Title == "Newly created task #1");

? ? ? ? ? ? ? ? task1.ShouldNotBeNull();

? ? ? ? ? ? ? ? task1.AssignedPersonId.ShouldBe(neo.Id);

? ? ? ? ? ? });

? ? ? ? }


? ? ? ? [Fact]

? ? ? ? public async System.Threading.Tasks.Task Should_Not_Create_New_Task_Without_Title()

? ? ? ? {

? ? ? ? ? ? await Assert.ThrowsAsync<AbpValidationException>(async () =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? await _taskAppService.Create(new CreateTaskInput

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? Title = null

? ? ? ? ? ? ? ? });

? ? ? ? ? ? });

? ? ? ? }

? ? }

}

第一個測試創建了一個帶 title 的任務, 第二個測試創建了一個帶 title 和 責任人 的測試,最后一個測試創建了一個無效的任務來展示 exception 例子。

?

任務創建頁面


我們現在知道 TaskAppService.Create 方法可以正常工作了。現在,我們可以創建一個頁面來添加新任務了。完成后的效果如下圖所示:

首先,我們在任務控制器 TaskController 添加一個 Create action 。

代碼如下

using System.Threading.Tasks;

using Abp.Application.Services.Dto;

using Acme.SimpleTaskApp.Tasks;

using Acme.SimpleTaskApp.Tasks.Dtos;

using Acme.SimpleTaskApp.Web.Models.Tasks;

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.Rendering;

using System.Linq;

using Acme.SimpleTaskApp.Common;

using Acme.SimpleTaskApp.Web.Models.People;


namespace Acme.SimpleTaskApp.Web.Controllers

{

? ? public class TasksController : SimpleTaskAppControllerBase

? ? {

? ? ? ? private readonly ITaskAppService _taskAppService;

? ? ? ? private readonly ILookupAppService _lookupAppService;


? ? ? ? public TasksController(

? ? ? ? ? ? ITaskAppService taskAppService,

? ? ? ? ? ? ILookupAppService lookupAppService)

? ? ? ? {

? ? ? ? ? ? _taskAppService = taskAppService;

? ? ? ? ? ? _lookupAppService = lookupAppService;

? ? ? ? }


? ? ? ? //...

? ? ? ??

? ? ? ? public async Task<ActionResult> Create()

? ? ? ? {

? ? ? ? ? ? var peopleSelectListItems = (await _lookupAppService.GetPeopleComboboxItems()).Items

? ? ? ? ? ? ? ? .Select(p => p.ToSelectListItem())

? ? ? ? ? ? ? ? .ToList();


? ? ? ? ? ? peopleSelectListItems.Insert(0, new SelectListItem { Value = string.Empty, Text = L("Unassigned"), Selected = true });


? ? ? ? ? ? return View(new CreateTaskViewModel(peopleSelectListItems));

? ? ? ? }

? ? }

}

我們將 ILookupAppService 反射進來,這樣可以獲取責任人下拉框的項目。本來我們是可以直接反射使用 IRepository<Person,Guid> 的,但是為了更好的分層和復用,我們還是使用 ILookUpAppService 。ILookupAppService.GetPeopleComboboxItems 在應用層的定義如下:

代碼如下

public interface ILookupAppService : IApplicationService

{

? ? Task&lt;ListResultDto&lt;ComboboxItemDto>> GetPeopleComboboxItems();

}


public class LookupAppService : SimpleTaskAppAppServiceBase, ILookupAppService

{

? ? private readonly IRepository&lt;Person, Guid> _personRepository;


? ? public LookupAppService(IRepository&lt;Person, Guid> personRepository)

? ? {

? ? ? ? _personRepository = personRepository;

? ? }


? ? public async Task&lt;ListResultDto&lt;ComboboxItemDto>> GetPeopleComboboxItems()

? ? {

? ? ? ? var people = await _personRepository.GetAllListAsync();

? ? ? ? return new ListResultDto&lt;ComboboxItemDto>(

? ? ? ? ? ? people.Select(p => new ComboboxItemDto(p.Id.ToString("D"), p.Name)).ToList()

? ? ? ? );

? ? }

}

ComboboxItemDto 是一個簡單的類(在 ABP 中定義),用于傳遞下拉框 Combobox 的項目的數據。 TaskController.Create 方法僅使用了這個方法并將返回的列表轉換為 SelectListItem (在 AspNet Core 中定義),然后用 CreateTaskViewModel 返回給視圖。

代碼如下

using System.Collections.Generic;

using Microsoft.AspNetCore.Mvc.Rendering;


namespace Acme.SimpleTaskApp.Web.Models.People

{

? ? public class CreateTaskViewModel

? ? {

? ? ? ? public List&lt;SelectListItem> People { get; set; }


? ? ? ? public CreateTaskViewModel(List&lt;SelectListItem> people)

? ? ? ? {

? ? ? ? ? ? People = people;

? ? ? ? }

? ? }

}

Create 視圖如下:

代碼如下

@using Acme.SimpleTaskApp.Web.Models.People

@model CreateTaskViewModel


@section scripts

{

? ? &lt;environment names="Development">

? ? ? ? &lt;script src="~/js/views/tasks/create.js">&lt;/script>

? ? &lt;/environment>


? ? &lt;environment names="Staging,Production">

? ? ? ? &lt;script src="~/js/views/tasks/create.min.js">&lt;/script>

? ? &lt;/environment>

}


&lt;h2>

? ? @L("NewTask")

&lt;/h2>


&lt;form id="TaskCreationForm">

? ??

? ? &lt;div class="form-group">

? ? ? ? &lt;label for="Title">@L("Title")&lt;/label>

? ? ? ? &lt;input type="text" name="Title" class="form-control" placeholder="@L("Title")" required maxlength="@Acme.SimpleTaskApp.Tasks.Task.MaxTitleLength">

? ? &lt;/div>


? ? &lt;div class="form-group">

? ? ? ? &lt;label for="Description">@L("Description")&lt;/label>

? ? ? ? &lt;input type="text" name="Description" class="form-control" placeholder="@L("Description")" maxlength="@Acme.SimpleTaskApp.Tasks.Task.MaxDescriptionLength">

? ? &lt;/div>


? ? &lt;div class="form-group">

? ? ? ? @Html.Label(L("AssignedPerson"))

? ? ? ? @Html.DropDownList(

? ? ? ? ? ? "AssignedPersonId",

? ? ? ? ? ? Model.People,

? ? ? ? ? ? new

? ? ? ? ? ? {

? ? ? ? ? ? ? ? @class = "form-control",

? ? ? ? ? ? ? ? id = "AssignedPersonCombobox"

? ? ? ? ? ? })

? ? &lt;/div>


? ? &lt;button type="submit" class="btn btn-default">@L("Save")&lt;/button>


&lt;/form>

我們編寫 create.js 如下:

代碼如下

(function($) {

? ? $(function() {


? ? ? ? var _$form = $('#TaskCreationForm');


? ? ? ? _$form.find('input:first').focus();


? ? ? ? _$form.validate();


? ? ? ? _$form.find('button[type=submit]')

? ? ? ? ? ? .click(function(e) {

? ? ? ? ? ? ? ? e.preventDefault();


? ? ? ? ? ? ? ? if (!_$form.valid()) {

? ? ? ? ? ? ? ? ? ? return;

? ? ? ? ? ? ? ? }


? ? ? ? ? ? ? ? var input = _$form.serializeFormToObject();

? ? ? ? ? ? ? ? abp.services.app.task.create(input)

? ? ? ? ? ? ? ? ? ? .done(function() {

? ? ? ? ? ? ? ? ? ? ? ? location.href = '/Tasks';

? ? ? ? ? ? ? ? ? ? });

? ? ? ? ? ? });

? ? });

})(jQuery);

讓我們一起來看看這個 javascript 代碼都做了什么:

  • 在表單里預先做好驗證(使用 jquery validation?插件)準備,并在保存 Save 按鈕被點擊后進行驗證。

  • 使用序列化表格為對象 serializeFormToObject 插件 (在解決方案中的 jquery-extensions.js 中定義), 將表格數據 forum data 轉換為 JSON 對象(我們將 jquery-extensions.js 添加到最后的腳本文件 _Layout.cshtml )

  • 使用 abp.services.task.create 方法調用 TaskAppService.Create 方法。這是 ABP 中的一個很重要的特性。我們可以在 javascript 代碼中使用應用服務,簡單的就想在代碼里直接調用 javascript 方法 (詳情請見 ?details)

最后,我們在任務列表頁面里添加一個 “添加任務 Add Task”按鈕,點擊后就可以導航到任務創建頁面:

代碼如下

1 &lt;a class="btn btn-primary btn-sm" asp-action="Create">@L("AddNew")&lt;/a>

刪除主頁和關于頁

如果我們不需要主頁和關于頁,我們可以從應用里刪除掉它們。首先,刪除主頁控制器 HomeController :

代碼如下

using Microsoft.AspNetCore.Mvc;


namespace Acme.SimpleTaskApp.Web.Controllers

{

? ? public class HomeController : SimpleTaskAppControllerBase

? ? {

? ? ? ? public ActionResult Index()

? ? ? ? {

? ? ? ? ? ? return RedirectToAction("Index", "Tasks");

? ? ? ? }

? ? }

}

然后刪除 視圖里的主頁 Views/Home 文件夾并從 SimpleTaskAppNavigationProvider 類里刪除菜單項。我們也可以從本地化 JSON 文件中刪除點不需要的關鍵詞。

?

其他相關內容

我們將不斷改進本篇內容

  • 從任務列表里打開/關閉任務,然后刷新任務項目。

  • 為責任人下拉框使用組件

  • 等等

?

文章更改歷史

  • 2017-07-30:將文章中的 ListResultOutput 替換為 ListResultDto

  • 2017-06-02:將項目和文章修改為支持 .net core

  • 2016-08-09:根據反饋修改文章

  • 2016-08-08:初次發布文章

相關文章:

  • 手把手引進門之 ASP.NET Core & Entity Framework Core(官方教程翻譯版 版本3.2.5)

  • ABP .Net Core Entity Framework遷移使用MySql數據庫

  • [52ABP實戰系列] .NET CORE實戰入門第三章更新了

  • ABP從入門到精通(1):aspnet-zero-core項目啟動及各項目源碼說明

  • ABP從入門到精通(2):aspnet-zero-core 使用MySql數據庫

  • ABP從入門到精通(3):aspnet-zero-core 使用Redis緩存

  • ABP從入門到精通(4):使用基于JWT標準的Token訪問WebApi

  • ABP從入門到精通(5):.擴展國際化語言資源

原文地址:http://www.cnblogs.com/yabu007/p/8117792.html


.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com

總結

以上是生活随笔為你收集整理的使用 ASP.NET Core, Entity Framework Core 和 ABP 创建N层Web应用 第二篇的全部內容,希望文章能夠幫你解決所遇到的問題。

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