C#与.NET技术
实践与教程

专注分享C#、ASP.NET Core、Azure、Blazor等微软技术栈的开发教程、架构设计和最佳实践

85
技术文章
42
教程系列
2.8k
月访问量
328
活跃读者
 阿里云容器镜像服务提交代码自动构建Docker镜像教程
后端
2020-12-15 0k

阿里云容器镜像服务提交代码自动构建Docker镜像教程

创建命名空间 创建仓库镜像 如果没有绑定账号,根据提示绑定一下账号,然后选择你的项目即可 镜像仓库–在对应的镜像 点击 【管理】 添加构建规则 这个镜像版本号建议 latest 在构建的时默认是latest 比如 # 默认 docker run -p 8000:80 --name mynginx -d nginx # 指定镜像版本 docker run -p 8000:80 --name mynginx -d nginx:1 现在每次提交代码到master会自动触发构建,也可以手动 立即构建

dotnet docker 阿里云
阅读更多
使用Docker部署Apollo多个环境在一台(Linux)服务器
后端
2020-04-05 0k

使用Docker部署Apollo多个环境在一台(Linux)服务器

一、搭建MySql 1、拉取官方镜像 (我们这里选择5.7,如果不写后面的版本号则会自动拉取最新版) docker pull mysql:5.7 # 拉取 mysql 5.7 docker pull mysql # 拉取最新版mysql镜像 docker images # 检查是否拉取成功 2、运行镜像 docker run -p 23306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7 参数说明: –name:容器名,此处命名为mysql -e:配置信息,此处配置mysql的root用户的登陆密码 -p:端口映射,此处映射 主机23306端口 到 容器的3306端口 3、创建数据库 根据\apollo\scripts\sql目录下的两个脚本 创建三个数据库 ApolloConfigDBPro : apolloconfigdb.sql ApolloConfigDBDev : apolloconfigdb.sql ApolloPortalDB : apolloportaldb.sql 4、修改值 修改 ApolloPortalDB库中ServerConfig表apollo.portal.envs的值为dev,pro 修改 ApolloConfigDBPro库中ServerConfig表eureka.service.url的值 为http://localhost:8083/eureka/ 二、搭建Apollo 拉取镜像 docker pull idoop/docker-apollo 运行容器 注意 “=” 附近不能有空格 docker run --net="host" --name myapollo -d \ -e PORTAL_DB='jdbc:mysql://192.

apollo docker 配置中心
阅读更多
C# AI Agent 选型与框架推荐 - 2025最新指南
后端
2025-11-07 0k

C# AI Agent 选型与框架推荐 - 2025最新指南

以下是 当前主流的开源 C# AI Agent 框架 / 项目(按成熟度、活跃度排序),涵盖通用 Agent 框架、垂直场景 Agent 实现,以及基于 C# 生态(.NET 6+)的轻量化方案,方便开发者直接复用或二次开发: 一、通用型开源 C# AI Agent 框架(核心推荐) 这类框架提供 Agent 的核心能力(任务规划、工具调用、记忆管理、多轮对话),支持对接主流大模型(OpenAI、本地 LLaMA/Phi 等),灵活性强。 1. Semantic Kernel(微软官方) 核心定位:.NET 生态首选的开源 AI Agent 开发框架(微软主导,跨语言支持 C#/Python/Java),专注于「将 AI 能力与传统代码、工具链融合」。 关键特性: 内置 Agent 生命周期管理(规划、执行、记忆、工具调用); 支持函数调用(工具注册)、向量数据库集成(记忆存储)、多轮对话上下文管理; 无缝对接 OpenAI、Azure OpenAI、本地大模型(通过 Ollama、LM Studio 适配); 丰富的插件生态(文件操作、HTTP 请求、数据库访问等现成工具)。 开源地址:https://github.com/microsoft/semantic-kernel 技术栈:.NET 6+(C# 10+),支持 Windows/macOS/Linux

AI Agent C#
阅读更多
WCF已退休?.NET分布式技术的历史演进与现代替代方案分析
后端
2025-11-07 0k

WCF已退休?.NET分布式技术的历史演进与现代替代方案分析

引言 提到 WCF(Windows Communication Foundation),很多.NET 开发者并不陌生 —— 它曾是.NET Framework 时代分布式系统通信的 “全能选手”。但如今,REST API、gRPC 等技术早已成为主流,有人疑惑:WCF 是不是已经没有意义了?它真的只是 “API 不普及时代的临时替代” 吗? 其实,WCF 的价值远比 “临时替代” 复杂。它的兴起源于特定时代的技术需求,衰落则是技术迭代的必然,而其设计思想至今仍在影响着现代分布式通信技术。今天我们就从时代背景、现状价值、技术替代三个维度,彻底说清 WCF 的 “前世今生”。 一、先澄清:WCF 不是 “临时 API 替代”,而是当年的 “全能服务框架” 很多人对 WCF 的认知停留在 “服务调用”,但这只是它的冰山一角。WCF 的核心定位是微软为.NET Framework 打造的「统一服务开发框架」,目标是让开发者用一套代码,兼容多种通信场景,而非简单替代早期功能单一的 WebService 或 ASHX。 它的核心能力远超 “基础 API 调用”: 多协议支持:覆盖 HTTP(REST/SOAP)、TCP、命名管道(Named Pipe)、MSMQ、WS-* 等,既能满足简单接口通信,也能支撑企业级复杂协议(如事务、安全、可靠消息); 多交互模式:支持请求 - 响应、单向通信、发布订阅、流传输(大文件 / 实时数据)等,适配不同分布式场景; 企业级特性:内置 Windows 认证、证书认证、自定义授权、数据加密、分布式事务等,无需额外开发即可满足企业级安全和一致性需求。 而早期的 “API”(如简单 WebService)功能单一、缺乏统一配置能力,WCF 的出现本质是为了解决「企业级分布式系统的复杂通信痛点」,而非 “临时过渡”。 二、现在的 WCF:新项目零价值,存量系统仍需 “续命” 1.

WCF 分布式技术 .NET
阅读更多
数据防篡改实用方案:从原理到落地实现 - 数字签名与HMAC详解
后端
2025-11-07 0k

数据防篡改实用方案:从原理到落地实现 - 数字签名与HMAC详解

数据防篡改实用方案:从原理到落地实现 在后端服务、文件传输、接口通信等场景中,数据完整性是不可忽视的安全底线 —— 无论是网络传输中的字节丢失,还是恶意攻击者篡改核心业务数据(如转账金额、订单信息),都可能引发严重的业务风险。 很多开发者知道用散列算法(如 SHA256)做数据校验,但容易陷入一个误区:如果篡改者同时修改数据和对应的散列值,接收方该如何识别?本文将从底层原理出发,拆解单纯散列校验的漏洞,通过简洁的代码示意,演示 “数字签名” 和 “HMAC” 两种防篡改方案,帮你快速落地数据完整性防护。 一、数据校验的核心逻辑:给数据加 “唯一指纹” 1. 基础原理 数据校验的本质是通过数学算法,将任意长度的原始数据转换为固定长度、独一无二的特征值(类似人类指纹),这个特征值被称为 “校验值”(散列值、校验位等)。 核心流程: 发送方:原始数据 → 校验算法(如 SHA256)→ 校验值 → 发送 “数据 + 校验值” 接收方:接收数据 → 相同校验算法 → 新校验值 → 对比 “新校验值” 与 “接收的校验值” 结论:一致则数据未篡改,不一致则数据异常 2. 散列算法的关键特性 散列算法是防篡改的基础,其安全性依赖两个核心特性: 抗碰撞性:不同数据几乎不可能生成相同散列值,哪怕修改 1 个字符(如 “123” 改为 “124”),散列值也会完全不同; 不可逆性:只能通过原始数据计算散列值,无法通过散列值反推原始数据。 3. 单纯散列校验的致命漏洞 单纯的 “数据 + 散列值” 方案存在明显缺陷:如果篡改者修改原始数据后,对篡改数据重新计算散列值,再将 “篡改数据 + 新散列值” 一起发送,接收方会误以为数据合法 —— 因为散列值本身没有 “身份标识”,无法证明是合法发送方生成的。

RSA HMAC 数字签名
阅读更多
赋能企业级开发:ABP vNext 高级实践与核心功能深入解析
后端
2024-12-03 0k

赋能企业级开发:ABP vNext 高级实践与核心功能深入解析

赋能企业级开发:ABP vNext 高级实践与核心功能深入解析 如果你认为 ABP vNext 只是一个帮你做 CRUD 的框架,那你就太小看它了。它是一个完整的、基于最佳实践的应用开发平台。本文将带你超越 Hello World,探索其如何在真实企业级项目中大放异彩。 在上一篇入门文章后,我们了解了 ABP 的基础。现在,让我们深入其更强大的功能,看看它如何解决实际开发中的复杂场景。 一、 模块化架构:构建复杂系统的基石 ABP 的模块化不仅是代码组织方式,更是架构设计哲学。 1. 定义与依赖 每个模块都是一个独立的单元。创建一个模块非常简单: [DependsOn( // 显式声明依赖 typeof(AbpAccountApplicationModule), typeof(AbpIdentityApplicationModule), typeof(YourModuleProjectDomainModule) // 依赖自己的领域层 )] public class YourModuleApplicationModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { // 配置本模块的服务 Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<YourModuleApplicationModule>("YourCompany.YourModule"); }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) { // 应用启动时的逻辑 var app = context.GetApplicationBuilder(); // ... 中间件配置 } } 2.

ABP vNext 企业级开发 模块化架构
阅读更多
架构反思:引入 Redis 统计在线人数后,JWT 是否还有存在的意义?
c#后端
2024-10-16 0k

架构反思:引入 Redis 统计在线人数后,JWT 是否还有存在的意义?

在构建 .NET Web API 后台时,为了实现在线人数统计,我们通常会引入 Redis 来存储用户的活跃状态。 这时,一个非常敏锐且直击架构本质的问题往往会浮出水面: “既然为了统计在线人数,每次请求都要去 Redis 读写一次,那 JWT ‘无状态、不查库’ 的核心优势不就没了吗?干脆用一个短小的随机字符串(普通 Token),既能节省流量,又能节省计算资源,岂不更好?” 这是一个极好的问题。它触及了软件架构的核心——权衡 (Trade-off)。 答案是:在特定场景下,普通 Token 确实更好;但在大多数现代架构中,JWT + Redis 的组合依然具有不可替代的优势。 本文将从依赖性、性能、架构扩展性三个维度,深入剖析为什么我们依然推荐保留 JWT。 1. 核心区别:强依赖 vs. 弱依赖 这是两者最根本的区别,决定了系统的可用性 (Availability) 上限。 🔵 方案 A:普通 Token (Reference Token) + Redis 在这个方案中,Token 只是一个无意义的随机字符串(一把钥匙)。 流程: 请求到达 -> 解析 Token -> 必须请求 Redis 查找对应的 UserID -> 拿到身份 -> 执行业务。 风险: 这里 Redis 是强依赖。 如果 Redis 宕机、网络抖动或连接数耗尽,整个系统的认证模块瞬间瘫痪。

JWT Redis 认证授权
阅读更多
从机器指令到面向对象:编程思想的演进与领域驱动设计
后端
2024-07-24 0k

从机器指令到面向对象:编程思想的演进与领域驱动设计

面对领域驱动设计(DDD)的火热,很多熟悉面向对象(OOP)的开发者都会有一个疑问:这和我以前学的OOP是什么关系?是全新的东西,还是旧瓶装新酒?本文将带你回顾编程思想的演进历程,理清DDD与OOP的深刻联系,并展望现代编程思想的融合之道。 一、编程思想的演进脉络:一部与“复杂性”的斗争史 编程的发展史,就是一部工程师们如何不断地抽象和封装,以应对日益增长的软件复杂性的历史。让我们用一张时间线来直观感受这一历程: timeline title 编程思想演进历程 1950年代以前 : 面向机器编程 : 主要与硬件直接交互 1950-1960年代 : 面向过程编程 : 以步骤为中心<br>结构化程序设计 1960-1970年代 : 面向对象编程兴起 : 对象为核心<br>封装、继承、多态 2000年代 : 领域驱动设计提出 : 应对业务复杂性<br>强调领域建模 2000年代至今 : 多种思想并存 : 函数式编程复兴<br>响应式编程等 下面我们来详细解读每个阶段的核心思想与突破: 面向机器与面向过程:效率与结构的初探 面向机器:在计算机诞生初期,编程直接使用机器语言或汇编语言,程序员必须深入了解硬件细节。这种方式效率极低,难以维护,是“与硬件共舞”的时代。 面向过程:随着高级语言(如Fortran, C)的出现,面向过程的思想成为主流。它将程序看作一系列线性步骤(过程),通过函数来实现每个环节,其核心方法是“自顶向下、逐步求精”。然而,当系统变得复杂时,数据和操作分离的特性使得代码的复用和维护变得异常困难,全局变量的滥用等问题凸显。 面向对象编程的兴起:将现实世界映射入代码 为了解耦数据与操作,面向对象编程(OOP) 应运而生。它以类和对象作为基本程序单位,将数据和对数据的操作封装在一起。它通过三大特性来管理复杂性: 封装:收敛内部逻辑,只暴露必要的接口。 继承:实现代码的复用和层次的抽象。 多态:允许同一接口表现出不同的行为,极大地提高了系统的灵活性。 OOP通过对象的概念将现实世界映射到软件系统,更符合人类的思维方式,是程序设计方法学上的一次巨大飞跃。 领域驱动设计的深化:聚焦业务复杂性的战略工具 到了2004年,埃里克·埃文斯(Eric Evans)在其著作《领域驱动设计》中提出了DDD。这时,软件的规模和技术复杂性已经得到较好控制,但业务逻辑本身的复杂性成为了新的挑战。 DDD的核心是建立正确的领域模型,并让这个模型成为项目成员(包括领域专家、设计人员、开发人员)之间沟通的通用语言。它在OOP的基础上,进一步强调了边界的控制,引入了限界上下文和聚合等核心模式,来应对大型、复杂业务系统的设计和拆分问题。 二、DDD与OOP:是继承与发展,而非颠覆与取代 领域驱动设计并非一个全新的编程范式,它可以看作是面向对象思想在复杂业务系统分析和设计上的进一步发展和规范化应用。 1. 核心理念的继承

编程思想 面向对象 OOP
阅读更多
.NET Core Web API 过滤器完全指南:类型、用途与实战
后端
2024-06-27 0k

.NET Core Web API 过滤器完全指南:类型、用途与实战

在 .NET Core Web API 开发中,过滤器(Filter)是实现横切关注点解耦的核心组件。它能在请求处理的不同阶段拦截逻辑,统一处理权限验证、日志记录、异常捕获等通用需求,避免重复代码。本文将详细拆解 .NET Core Web API 内置的 5 类过滤器,分析其执行机制、核心用途,并提供可直接复用的实战示例。 一、过滤器核心概念:什么是过滤器? 过滤器是 .NET Core 提供的请求拦截机制,本质是一套 “钩子(Hook)” 系统,能嵌入请求处理生命周期的关键节点。其核心价值在于: 解耦横切关注点:将日志、权限、异常处理等通用逻辑与业务逻辑分离; 统一管控:对所有接口或指定接口批量应用通用规则; 可扩展性:支持自定义过滤器,满足复杂业务场景。 所有过滤器遵循固定的执行顺序,从请求进入到响应返回,完整流程如下: 授权过滤器 → 资源过滤器 → 模型绑定 → Action 过滤器 → 执行 Action → Action 过滤器 → 结果过滤器 → 执行结果 → 结果过滤器 → 资源过滤器 → 响应返回 若任意阶段抛出异常,将直接触发异常过滤器处理。 二、5 类内置过滤器详解:用途 + 执行时机 1. 授权过滤器(AuthorizationFilter):请求的 “第一道关卡” 核心作用 验证请求是否有权限访问接口,是所有过滤器中执行最早的组件。若验证失败,直接终止请求并返回 401(未授权)或 403(禁止访问),不执行后续逻辑。 内置实现 [Authorize]:标记需要授权的 Action/Controller,依赖 IAuthorizationService 校验身份(配合 JWT、Cookie 等认证方案)。支持角色、策略等精细化授权,示例: // 仅 Admin 角色可访问 [Authorize(Roles = "Admin")] [HttpGet("admin/data")] public IActionResult GetAdminData() => Ok("管理员数据"); [AllowAnonymous]:允许匿名访问,用于公开接口(如登录、注册),会跳过授权校验: [AllowAnonymous] [HttpPost("login")] public IActionResult Login(string username, string password) => Ok("登录成功"); 关键特点 仅关注 “是否允许访问”,不处理业务逻辑;

dotnet webapi filter
阅读更多
string 类型深度解析:是值类型还是引用类型?
后端
2024-06-27 0k

string 类型深度解析:是值类型还是引用类型?

在 .NET 开发中,string 类型是我们日常使用最频繁的类型之一。但一个常见的疑问始终困扰着不少开发者:string 到底是值类型还是引用类型?其实答案很明确 ——string 本质是引用类型,但它兼具部分值类型的特性,这种特殊性也让它成为了 .NET 类型系统中极具代表性的存在。本文将从本质定义、特殊特性到实际验证,全面解析 string 类型的本质。 一、核心结论:string 是引用类型 从 .NET 类型系统的底层设计来看,string 完全满足引用类型的所有定义,这是不可动摇的核心事实。 1. 引用类型的核心判定依据 继承关系:string 直接继承自 System.Object(引用类型的基类),而非值类型专属的 System.ValueType; 存储位置:string 对象的实际内容存储在 托管堆 中,变量仅持有指向堆内存的 “引用地址”(类似指针); 可空性:string 变量默认支持赋值为 null(表示不指向任何堆内存对象),而值类型(如 int、DateTime)默认非空,需通过 Nullable<T>(如 int?)才能支持 null; 引用传递特性:当把一个 string 变量赋值给另一个变量时,传递的是 “引用地址”,而非字符串内容的拷贝。 2. 代码验证:引用类型的本质 // 1. 变量 a 指向堆内存中的 "hello" 对象 string a = "hello"; // 2. 变量 b 拷贝的是 a 的引用地址,而非 "hello" 内容 string b = a; // 验证:a 和 b 指向同一个堆内存对象 Console.

string 值类型 引用类型
阅读更多