基於 abp vNext 和 .NET Core 開發博客項目 - 博客介面實戰篇(三)

来源:https://www.cnblogs.com/meowv/archive/2020/06/05/13039883.html
-Advertisement-
Play Games

系列文章 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場 ...


系列文章

  1. 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目
  2. 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來
  3. 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場
  4. 基於 abp vNext 和 .NET Core 開發博客項目 - 數據訪問和代碼優先
  5. 基於 abp vNext 和 .NET Core 開發博客項目 - 自定義倉儲之增刪改查
  6. 基於 abp vNext 和 .NET Core 開發博客項目 - 統一規範API,包裝返回模型
  7. 基於 abp vNext 和 .NET Core 開發博客項目 - 再說Swagger,分組、描述、小綠鎖
  8. 基於 abp vNext 和 .NET Core 開發博客項目 - 接入GitHub,用JWT保護你的API
  9. 基於 abp vNext 和 .NET Core 開發博客項目 - 異常處理和日誌記錄
  10. 基於 abp vNext 和 .NET Core 開發博客項目 - 使用Redis緩存數據
  11. 基於 abp vNext 和 .NET Core 開發博客項目 - 集成Hangfire實現定時任務處理
  12. 基於 abp vNext 和 .NET Core 開發博客項目 - 用AutoMapper搞定對象映射
  13. 基於 abp vNext 和 .NET Core 開發博客項目 - 定時任務最佳實戰(一)
  14. 基於 abp vNext 和 .NET Core 開發博客項目 - 定時任務最佳實戰(二)
  15. 基於 abp vNext 和 .NET Core 開發博客項目 - 定時任務最佳實戰(三)
  16. 基於 abp vNext 和 .NET Core 開發博客項目 - 博客介面實戰篇(一)
  17. 基於 abp vNext 和 .NET Core 開發博客項目 - 博客介面實戰篇(二)

上篇文章完成了分類和標簽頁面相關的共6個介面,本篇繼續來寫博客增刪改查API的業務。

供前端查詢用的介面還剩下一個,這裡先補上。

友鏈列表

0

分析:返回標題和對應的鏈接即可,傳輸對象FriendLinkDto.cs

//FriendLinkDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class FriendLinkDto
    {
        /// <summary>
        /// 標題
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 鏈接
        /// </summary>
        public string LinkUrl { get; set; }
    }
}

添加查詢友鏈列表介面和緩存介面。

//IBlogService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog
{
    public partial interface IBlogService
    {
        /// <summary>
        /// 查詢友鏈列表
        /// </summary>
        /// <returns></returns>
        Task<ServiceResult<IEnumerable<FriendLinkDto>>> QueryFriendLinksAsync();
    }
}
//IBlogCacheService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Caching.Blog
{
    public partial interface IBlogCacheService
    {
        /// <summary>
        /// 查詢友鏈列表
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>
        Task<ServiceResult<IEnumerable<FriendLinkDto>>> QueryFriendLinksAsync(Func<Task<ServiceResult<IEnumerable<FriendLinkDto>>>> factory);
    }
}

接下來,實現他們。

//BlogCacheService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Meowv.Blog.Domain.Shared.MeowvBlogConsts;

namespace Meowv.Blog.Application.Caching.Blog.Impl
{
    public partial class BlogCacheService
    {
        private const string KEY_QueryFriendLinks = "Blog:FriendLink:QueryFriendLinks";

        /// <summary>
        /// 查詢友鏈列表
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<FriendLinkDto>>> QueryFriendLinksAsync(Func<Task<ServiceResult<IEnumerable<FriendLinkDto>>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_QueryFriendLinks, factory, CacheStrategy.ONE_DAY);
        }
    }
}
//BlogService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.Domain.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog.Impl
{
    public partial class BlogService
    {
        /// <summary>
        /// 查詢友鏈列表
        /// </summary>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<FriendLinkDto>>> QueryFriendLinksAsync()
        {
            return await _blogCacheService.QueryFriendLinksAsync(async () =>
            {
                var result = new ServiceResult<IEnumerable<FriendLinkDto>>();

                var friendLinks = await _friendLinksRepository.GetListAsync();
                var list = ObjectMapper.Map<IEnumerable<FriendLink>, IEnumerable<FriendLinkDto>>(friendLinks);

                result.IsSuccess(list);
                return result;
            });
        }
    }
}

直接查詢所有的友鏈數據,這裡使用前面講到的AutoMapper處理對象映射,將IEnumerable<FriendLink>轉換為IEnumerable<FriendLinkDto>

MeowvBlogAutoMapperProfile.cs中添加一條配置:CreateMap<FriendLink, FriendLinkDto>();,在BlogController中添加API。

/// <summary>
/// 查詢友鏈列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("friendlinks")]
public async Task<ServiceResult<IEnumerable<FriendLinkDto>>> QueryFriendLinksAsync()
{
    return await _blogService.QueryFriendLinksAsync();
}

編譯運行,打開查詢友鏈的API,此時沒數據,手動添加幾條數據進去再試試吧。

2

文章管理

3

後臺文章管理包含:文章列表、新增、更新、刪除文章,接下來依次完成這些介面。

文章列表

這裡的文章列表和前臺的文章列表差不多,就是多了一個Id,以供編輯和刪除使用,所以可以新建一個模型類QueryPostForAdminDto繼承QueryPostDto,添加PostBriefForAdminDto繼承PostBriefDto同時新增一個欄位主鍵Id。

QueryPostForAdminDto中隱藏基類成員Posts,使用新的接收類型:IEnumerable<PostBriefForAdminDto>

//PostBriefForAdminDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class PostBriefForAdminDto : PostBriefDto
    {
        /// <summary>
        /// 主鍵
        /// </summary>
        public int Id { get; set; }
    }
}
//QueryPostForAdminDto.cs
using System.Collections.Generic;

namespace Meowv.Blog.Application.Contracts.Blog
{
    public class QueryPostForAdminDto : QueryPostDto
    {
        /// <summary>
        /// Posts
        /// </summary>
        public new IEnumerable<PostBriefForAdminDto> Posts { get; set; }
    }
}

添加分頁查詢文章列表的介面:QueryPostsForAdminAsync(),關於後臺的一些介面就不添加緩存了。

//IBlogService.Admin.cs
using Meowv.Blog.Application.Contracts;
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog
{
    public partial interface IBlogService
    {
        /// <summary>
        /// 分頁查詢文章列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<ServiceResult<PagedList<QueryPostForAdminDto>>> QueryPostsForAdminAsync(PagingInput input);
    }
}

然後實現這個介面。

//BlogService.Admin.cs
using Meowv.Blog.Application.Contracts;
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using Meowv.Blog.ToolKits.Extensions;
using System.Linq;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog.Impl
{
    public partial class BlogService
    {
        /// <summary>
        /// 分頁查詢文章列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<ServiceResult<PagedList<QueryPostForAdminDto>>> QueryPostsForAdminAsync(PagingInput input)
        {
            var result = new ServiceResult<PagedList<QueryPostForAdminDto>>();

            var count = await _postRepository.GetCountAsync();

            var list = _postRepository.OrderByDescending(x => x.CreationTime)
                                      .PageByIndex(input.Page, input.Limit)
                                      .Select(x => new PostBriefForAdminDto
                                      {
                                          Id = x.Id,
                                          Title = x.Title,
                                          Url = x.Url,
                                          Year = x.CreationTime.Year,
                                          CreationTime = x.CreationTime.TryToDateTime()
                                      })
                                      .GroupBy(x => x.Year)
                                      .Select(x => new QueryPostForAdminDto
                                      {
                                          Year = x.Key,
                                          Posts = x.ToList()
                                      }).ToList();

            result.IsSuccess(new PagedList<QueryPostForAdminDto>(count.TryToInt(), list));
            return result;
        }
    }
}

實現邏輯也非常簡單和之前一樣,就是在Select的時候多了一個Id,添加一個新的Controller:BlogController.Admin.cs,添加這個介面。

//BlogController.Admin.cs
using Meowv.Blog.Application.Contracts;
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using static Meowv.Blog.Domain.Shared.MeowvBlogConsts;

namespace Meowv.Blog.HttpApi.Controllers
{
    public partial class BlogController
    {
        /// <summary>
        /// 分頁查詢文章列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet]
        [Authorize]
        [Route("admin/posts")]
        [ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
        public async Task<ServiceResult<PagedList<QueryPostForAdminDto>>> QueryPostsForAdminAsync([FromQuery] PagingInput input)
        {
            return await _blogService.QueryPostsForAdminAsync(input);
        }
    }
}

因為是後臺的介面,所以加上AuthorizeAttribute,指定介面組為GroupName_v2,參數方式為[FromQuery]

當沒有進行授權的時候,是無法訪問介面的。

4

新增文章

5

在做新增文章的時候要註意幾點,不是單純的添加文章數據就結束了,要指定文章分類,添加文章的標簽。添加標簽我這裡是從標簽庫中去取得數據,只存標簽Id,所以添加標簽的時候就可能存在添加了標簽庫中已有的標簽。

新建一個新增和更新文章的通用輸出參數模型類,起名:EditPostInput,繼承PostDto,然後添加標簽Tags欄位,返回類型IEnumerable<string>

//EditPostInput.cs
using System.Collections.Generic;

namespace Meowv.Blog.Application.Contracts.Blog.Params
{
    public class EditPostInput : PostDto
    {
        /// <summary>
        /// 標簽列表
        /// </summary>
        public IEnumerable<string> Tags { get; set; }
    }
}

添加新增文章的介面:InsertPostAsync

/// <summary>
/// 新增文章
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> InsertPostAsync(EditPostInput input);

然後去實現這個介面,實現之前,配置AutoMapper實體映射。

CreateMap<EditPostInput, Post>().ForMember(x => x.Id, opt => opt.Ignore());

EditPostInput轉換為Post,並且忽略Id欄位。

/// <summary>
/// 新增文章
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ServiceResult> InsertPostAsync(EditPostInput input)
{
    var result = new ServiceResult();

    var post = ObjectMapper.Map<EditPostInput, Post>(input);
    post.Url = $"{post.CreationTime.ToString(" yyyy MM dd ").Replace(" ", "/")}{post.Url}/";
    await _postRepository.InsertAsync(post);

    var tags = await _tagRepository.GetListAsync();

    var newTags = input.Tags
                       .Where(item => !tags.Any(x => x.TagName.Equals(item)))
                       .Select(item => new Tag
                       {
                           TagName = item,
                           DisplayName = item
                       });
    await _tagRepository.BulkInsertAsync(newTags);

    var postTags = input.Tags.Select(item => new PostTag
    {
        PostId = post.Id,
        TagId = _tagRepository.FirstOrDefault(x => x.TagName == item).Id
    });
    await _postTagRepository.BulkInsertAsync(postTags);

    result.IsSuccess(ResponseText.INSERT_SUCCESS);
    return result;
}

URL欄位,根據創建時間按照yyyy/MM/dd/name/格式拼接。

然後找出是否有新標簽,有的話批量添加至標簽表。

再根據input.Tags構建PostTag列表,也進行批量保存,這樣才算是新增好一篇文章,最後輸出ResponseText.INSERT_SUCCESS常量,提示成功。

BlogController.Admin.cs添加API。

/// <summary>
/// 新增文章
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
[Authorize]
[Route("post")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> InsertPostAsync([FromBody] EditPostInput input)
{
    return await _blogService.InsertPostAsync(input);
}

6

更新文章

更新操作和新增操作輸入參數一樣,只新增一個Id用來標識更新那篇文章,添加UpdatePostAsync更新文章介面。

/// <summary>
/// 更新文章
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> UpdatePostAsync(int id, EditPostInput input);

同樣的實現這個介面。

/// <summary>
/// 更新文章
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ServiceResult> UpdatePostAsync(int id, EditPostInput input)
{
    var result = new ServiceResult();

    var post = await _postRepository.GetAsync(id);
    post.Title = input.Title;
    post.Author = input.Author;
    post.Url = $"{input.CreationTime.ToString(" yyyy MM dd ").Replace(" ", "/")}{input.Url}/";
    post.Html = input.Html;
    post.Markdown = input.Markdown;
    post.CreationTime = input.CreationTime;
    post.CategoryId = input.CategoryId;

    await _postRepository.UpdateAsync(post);

    var tags = await _tagRepository.GetListAsync();

    var oldPostTags = from post_tags in await _postTagRepository.GetListAsync()
                      join tag in await _tagRepository.GetListAsync()
                      on post_tags.TagId equals tag.Id
                      where post_tags.PostId.Equals(post.Id)
                      select new
                      {
                          post_tags.Id,
                          tag.TagName
                      };

    var removedIds = oldPostTags.Where(item => !input.Tags.Any(x => x == item.TagName) &&
                                               tags.Any(t => t.TagName == item.TagName))
                                .Select(item => item.Id);
    await _postTagRepository.DeleteAsync(x => removedIds.Contains(x.Id));

    var newTags = input.Tags
                       .Where(item => !tags.Any(x => x.TagName == item))
                       .Select(item => new Tag
                       {
                           TagName = item,
                           DisplayName = item
                       });
    await _tagRepository.BulkInsertAsync(newTags);

    var postTags = input.Tags
                        .Where(item => !oldPostTags.Any(x => x.TagName == item))
                        .Select(item => new PostTag
                        {
                            PostId = id,
                            TagId = _tagRepository.FirstOrDefault(x => x.TagName == item).Id
                        });
    await _postTagRepository.BulkInsertAsync(postTags);

    result.IsSuccess(ResponseText.UPDATE_SUCCESS);
    return result;
}

ResponseText.UPDATE_SUCCESS是常量更新成功。

先根據Id查詢到資料庫中的這篇文章數據,然後根據input參數,修改需要修改的數據,最後保存。

註意的是,如果修改的時候修改了標簽,有可能新增也有可能刪除,也許會又有新增又有刪除。

這時候就需要註意,這裡做了一個比較通用的方法,找到資料庫中當前文章Id的所有Tags,然後根據參數input.Tags可以找出被刪掉的標簽的PostTags的Id,調用刪除方法刪掉即可,同時也可以獲取到新增的標簽,批量進行保存。

完成上面操作後,才保存新加標簽與文章對應的數據,最後提示更新成功,在BlogController.Admin添加API。

/// <summary>
/// 更新文章
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
[HttpPut]
[Authorize]
[Route("post")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> UpdatePostAsync([Required] int id, [FromBody] EditPostInput input)
{
    return await _blogService.UpdatePostAsync(id, input);
}

[HttpPut]指定請求方式為put請求,一般需要修改用put,添加用post。

[Required]指定參數id必填且是FromQuery的方式,input為[FromBody]

更新一下上面新增的數據試試。

7

8

刪除文章

刪除相對來說就非常簡單了,一般刪除都會做邏輯刪除,就是避免某些手殘刪除了,有找回的餘地,我們這裡就直接Delete了,也沒什麼重要數據。

添加介面:DeletePostAsync

/// <summary>
/// 刪除文章
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<ServiceResult> DeletePostAsync(int id);

實現介面。

/// <summary>
/// 刪除文章
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<ServiceResult> DeletePostAsync(int id)
{
    var result = new ServiceResult();

    var post = await _postRepository.GetAsync(id);
    if (null == post)
    {
        result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
        return result;
    }

    await _postRepository.DeleteAsync(id);
    await _postTagRepository.DeleteAsync(x => x.PostId == id);

    result.IsSuccess(ResponseText.DELETE_SUCCESS);
    return result;
}

刪除的時候同樣去查詢一下數據,來判斷是否存在。

ResponseText.DELETE_SUCCESS是添加的常量刪除成功,刪除成功同時也要將post_tags表的標簽對應關係也幹掉才算完整,在BlogController.Admin添加API。

/// <summary>
/// 刪除文章
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[Authorize]
[Route("post")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> DeletePostAsync([Required] int id)
{
    return await _blogService.DeletePostAsync(id);
}

[HttpDelete]指定請求方式是刪除資源,[Required]指定參數Id必填。

刪掉上面添加的文章看看效果。

9

至此,完成了博客文章的增刪改介面,未完待續...

開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial


搭配下方課程學習更佳 ↓ ↓ ↓

http://gk.link/a/10iQ7

7


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • /// <summary> /// Json字元串首字母轉大寫 /// </summary> /// <param name="strJsonData">json字元串</param> /// <returns></returns> public static string UpperFirst(s ...
  • 1、假設根目錄下存在兩個Html文件。一個是Source.html,一個是Target.html. Source.html Target.html內容為空。 目的:讀取Source.html中的<Font>標簽插入到Target.html中。 using HtmlAgilityPack; using ...
  • static void BinaryWriterReaderDemo() { using (FileStream fs = new FileStream("fs.txt", FileMode.Create)) { SaveData(fs); } using(FileStream fs=new Fil ...
  • 0. 前言 在之前的4篇的內容里,我們較為詳細的介紹了路由以及控制器還有視圖之間的關係。也就是說,系統如何從用戶的HTTP請求解析到控制器里,然後在控制器里處理數據,並返回給視圖,在視圖中顯示出來。這一篇我將為大家介紹基礎的最後一部分,佈局頁和靜態資源引入。 1. 佈局頁 在控制器和視圖那一篇,我們 ...
  • WebForm中路由的淵源 大多數做過.NET WEB FORM的人,第一次接觸ASP.NET MVC, 對路由可能很陌生,在他們的潛意識裡訪問一個.Net Web 應用程式是這樣的。 從訪問地址我們能看出,對應的是一個物理地址Index.aspx文件,是一對一的物理關係。假如我們在後期更改文件名稱 ...
  • 本次部署,基於docker進行,拉取的mcr.microsoft.com/dotnet/core/aspnet:3.1鏡像。 這裡,我沒有使用Dockerfile文件來創建鏡像。 Interop+OpenSsl+SslException: SSL Handshake failed with Open ...
  • 本文由葡萄城技術團隊於博客園原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 .Net Core 是什麼? 最新.Net Core 熱詞霸占了各個技術熱搜,看來微軟近年來發佈的 .Net Core 還是成功了引起了大家的熱烈討論。如果您對微軟熟悉 ...
  • 前言 目前Magicodes.IE更新到了2.2.3,感謝大家的支持,同時建議大家在使用過程中如果遇到一些問題或者說需要一些額外的功能可以直接提issues,當然更建議大家提PR。 近期更新 2020.05.24 【Nuget】版本更新到2.2.2 【Excel導入】增加了stream導入擴展方法 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...