【ASP.NET CORE】 3. 依赖注入、缓存

Asp.net Core3.1写的一个后台API框架,可以拿去直接用的】 Asp.net Core3.1写的一个后台API框架,可以拿去直接用的,也可以迭代至.net5 EFCore 2.跨域配置 3 JWT鉴权授权 4 Autofac实现依赖注入 5 redis封装 6 Automap配置等 编号:32199651364893996Genius小鱼 阅读详情

本系列专栏基于杨中科老师的《ASP.NET Core技术内幕与项目实战》,本人记录梳理的学习笔记,有部分的增补和省略。更全面系统的讲解,请看杨老师的视频课:【.NET教程,.Net Core视频教程,杨中科主讲】。

一、依赖注入

依赖注入(DI)是ASP.NET Core 框架的核心设计思想之一,它解决了代码耦合度高、测试困难、扩展性差等问题,是实现 “控制反转(IoC)” 的主流方式。

1. 使用方式

ASP.NET Core 框架内置了依赖注入容器,无需手动管理容器生命周期,核心使用方式分为以下三类:

(1)服务注册

在Program.cs中,builder.Build()执行前(容器构建完成前),通过builder.Services(本质是IServiceCollection)注册服务,框架会自动管理IServiceProvider(服务提供器)的创建和销毁。

// Program.cs 示例
var builder = WebApplication.CreateBuilder(args);

// 注册服务:指定生命周期(瞬时/作用域/单例)
builder.Services.AddScoped<IBookService, BookService>(); // 作用域(每次请求创建一个实例)
builder.Services.AddTransient<ILoggerHelper, LoggerHelper>(); // 瞬时(每次获取创建新实例)
builder.Services.AddSingleton<ICacheHelper, CacheHelper>(); // 单例(全局唯一实例)

var app = builder.Build();
(2)控制器构造函数注入(推荐)

这是最常用的注入方式,框架会自动识别控制器构造函数中的依赖并注入。

public class BookController : Controller
{
    private readonly IBookService _bookService;
    
    // 构造函数注入:框架自动解析IBookService的实现类
    public BookController(IBookService bookService)
    {
        _bookService = bookService;
    }
    
    [HttpGet]
    public IActionResult GetBooks()
    {
        var books = _bookService.GetAll();
        return Ok(books);
    }
}
(3)Action 参数注入

使用[FromServices],仅适用于单个 Action需要的服务,且满足 “调用频率低、实例创建成本高” 的场景(如大对象、数据库重型连接)。

注意:[FromServices]仅支持 Action 方法参数,普通类 / 方法不支持,且与 Action 其他参数(如 [FromQuery]、[FromBody])不冲突。

[HttpGet("{id}")]
public IActionResult GetBookById(
    int id, 
    [FromServices] IHeavyDataService heavyDataService) // 仅当前Action使用
{
    var book = heavyDataService.GetBookById(id);
    return Ok(book);
}

2. 模块化的服务注册框架

当项目规模扩大(多模块 / 多项目)时,分散在Program.cs的服务注册会变得混乱,杨老师编写的Zack.Commons通过模块化思想解决这一问题。

(1)使用步骤
  1. 安装 NuGet 包:
    Install-Package Zack.Commons
    # 或使用CLI
    dotnet add package Zack.Commons
  2. 每个模块 / 项目中创建实现IModuleInitializer的类,封装当前模块的服务注册逻辑:
    // 图书模块的注册类(BookModule/ModuleInitializer.cs)
    public class BookModuleInitializer : IModuleInitializer
    {
        public void Initialize(IServiceCollection services)
        {
            // 仅注册图书模块相关服务,职责单一
            services.AddScoped<IBookService, BookService>();
            services.AddScoped<IBookRepository, BookRepository>();
        }
    }
    
    // 订单模块的注册类(OrderModule/ModuleInitializer.cs)
    public class OrderModuleInitializer : IModuleInitializer
    {
        public void Initialize(IServiceCollection services)
        {
            services.AddScoped<IOrderService, OrderService>();
        }
    }
  3. 全局统一初始化所有模块:
    // Program.cs
    var builder = WebApplication.CreateBuilder(args);
    
    // 获取所有引用的程序集,自动扫描IModuleInitializer实现类
    var assemblies = ReflectionHelper.GetAllReferencedAssemblies();
    // 执行所有模块的初始化方法,自动注册服务
    builder.Services.RunModuleInitializers(assemblies);
    
    var app = builder.Build();
(2)核心原理
  • 反射:ReflectionHelper.GetAllReferencedAssemblies()会遍历当前应用所有引用的程序集,通过反射查找所有实现了IModuleInitializer接口的类;
  • 扩展方法:RunModuleInitializers是IServiceCollection的扩展方法,内部会创建这些实现类的实例,并调用Initialize方法,完成模块化的服务注册;
  • 优势:将服务注册逻辑分散到各模块,符合 “单一职责” 原则,降低Program.cs的复杂度,便于团队协作和维护。

二、缓存

缓存是系统性能优化的 “低成本高收益” 手段,核心思想是将高频访问、计算 / 查询成本高的数据暂存到快速存储介质中,减少重复计算 / 数据库查询,降低服务器压力。数据库索引、浏览器缓存本质上都是缓存思想的体现。

1. 客户端缓存

ResponseCacheAttribute,通过[ResponseCache]特性控制 HTTP 响应头的cache-control字段,指导浏览器 / 客户端缓存响应内容,减少重复请求。

// 示例:客户端缓存60秒,仅GET/HEAD请求生效
[HttpGet]
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
public IActionResult GetBooks()
{
    var books = _bookService.GetAll();
    return Ok(books);
}

核心参数:

  • Duration:缓存时长(秒),对应cache-control: max-age=60;
  • Location:缓存位置(Client/Any/Never);
  • NoStore:是否禁止缓存(true 时cache-control: no-store)。

2. 服务器端响应缓存

ResponseCaching 中间件,客户端缓存依赖浏览器遵守规范,服务器端缓存则是将响应内容暂存到服务器内存,对相同请求直接返回缓存内容,进一步降低服务器处理压力。

(1)使用步骤
  1. 注册服务(ASP.NET Core 3.x 默认已包含,无需额外安装):
    // Program.cs
    builder.Services.AddResponseCaching();
  2. 启用中间件(注意顺序:UseCors需在UseResponseCaching之前):
    var app = builder.Build();
    
    app.UseCors(); // 跨域中间件在前
    app.UseResponseCaching(); // 响应缓存中间件
    app.MapControllers();
  3. 结合[ResponseCache]特性使用:
    [HttpGet]
    // Duration>0时,服务器+客户端均缓存60秒
    [ResponseCache(Duration = 60)]
    public IActionResult GetBooks()
    {
        var books = _bookService.GetAll();
        return Ok(books);
    }
(2)服务器端缓存限制
  • 仅缓存状态码为 200 的GET/HEAD请求;
  • 响应头包含Authorization、Set-Cookie、Vary: *等时,不缓存;
  • 大部分浏览器遵守 RFC7324 规范,测试时可通过 Postman 禁用客户端缓存,验证服务器端缓存效果。

3. 内存缓存

内存缓存(IMemoryCache接口)是ASP.NET Core 最常用的缓存方式,将数据存储在应用程序进程内存中,读写速度极快,但数据与进程绑定(程序重启 / 回收则缓存丢失),且多站点 / 多进程间不共享。

(1)启用内存缓存
// Program.cs
builder.Services.AddMemoryCache(); // 注册内存缓存服务
(2)核心接口
方法 / 属性作用
TryGetValue(string key, out T value)尝试获取缓存,返回是否存在
Remove(string key)删除指定缓存项
Set(string key, T value, CacheEntryOptions options)设置缓存(指定过期策略)
GetOrCreate/GetOrCreateAsync核心方法:获取缓存,不存在则执行工厂方法创建并缓存
(3)核心用法

GetOrCreateAsync,有则获取,没有则创建。

public class BookService
{
    private readonly IMemoryCache _memCache;
    private readonly AppDbContext _dbCtx;
    private readonly ILogger<BookService> _logger;
    
    public BookService(IMemoryCache memCache, AppDbContext dbCtx, ILogger<BookService> logger)
    {
        _memCache = memCache;
        _dbCtx = dbCtx;
        _logger = logger;
    }
    
    public async Task<Book[]> GetBooksAsync()
    {
        _logger.LogInformation("开始执行GetBooksAsync");
        
        // 核心逻辑:缓存键为"AllBooks",不存在则从数据库查询并缓存
        var books = await _memCache.GetOrCreateAsync("AllBooks", async (cacheEntry) =>
        {
            _logger.LogInformation("缓存未命中,从数据库读取数据");
            // 设置缓存过期策略(可选)
            cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5); // 绝对过期5分钟
            return await _dbCtx.Books.ToArrayAsync();
        });
        
        _logger.LogInformation("返回数据给调用者");
        return books;
    }
}

执行逻辑:

  • 第一次调用:缓存未命中 → 执行工厂方法(数据库查询)→ 结果存入缓存 → 返回数据;
  • 后续调用:缓存命中 → 直接返回缓存数据,跳过数据库查询。

4. 缓存过期策略

内存缓存默认永久有效(直到程序重启),需通过过期策略解决 “缓存数据不一致” 问题,核心分为两类:

(1)绝对过期时间

AbsoluteExpiration,缓存项在指定时间点 / 时间段后强制过期,适用于 “数据更新频率固定” 的场景(如每日更新的榜单)。

var books = await _memCache.GetOrCreateAsync("AllBooks", async (cacheEntry) =>
{
    // 绝对过期:5分钟后强制失效
    cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
    return await _dbCtx.Books.ToArrayAsync();
});
(2)滑动过期时间

SlidingExpiration,缓存项在最后一次访问后,若超过指定时间未被访问则过期,适用于 “访问频率不稳定” 的场景(如用户个人数据)。

var books = await _memCache.GetOrCreateAsync("AllBooks", async (cacheEntry) =>
{
    // 滑动过期:3分钟内未访问则失效
    cacheEntry.SlidingExpiration = TimeSpan.FromMinutes(3);
    return await _dbCtx.Books.ToArrayAsync();
});
(3)混合过期(推荐)

结合绝对过期 + 滑动过期,既避免缓存长期不失效(滑动过期),又避免缓存频繁失效(绝对过期):

cacheEntry.SlidingExpiration = TimeSpan.FromMinutes(3); // 3分钟未访问则过期
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1); // 最多缓存1小时

5. 缓存常见问题及解决方案

(1)缓存穿透

问题:恶意请求不存在的 Key(如查询 ID=-1 的图书),缓存未命中后持续穿透到数据库,导致数据库压力过大。

解决方案:将 “查询结果为空” 也存入缓存(设置短过期时间),避免每次都查询数据库。

public async Task<Book?> GetBookByIdAsync(int id)
{
    var cacheKey = $"Book_{id}";
    var book = await _memCache.GetOrCreateAsync(cacheKey, async (cacheEntry) =>
    {
        // 空结果也缓存,设置短过期时间(如1分钟)
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
        _logger.LogInformation("查询数据库:Book_{0}", id);
        return await _dbCtx.Books.FindAsync(id); // 可能返回null
    });
    return book;
}

关键:GetOrCreateAsync会将null视为合法值存入缓存,避免重复查询不存在的数据。

(2)缓存雪崩

问题:大量缓存项在同一时间过期,导致大量请求瞬间穿透到数据库,引发数据库压力骤增甚至宕机。

解决方案:

  1. 为缓存项设置随机过期时间(核心):在基础过期时间上增加随机偏移(如 5±1 分钟),避免批量过期;
  2. 分级缓存(内存缓存 + 分布式缓存);
  3. 熔断 / 限流(如使用 Polly 限制数据库请求量)。

示例(随机过期时间):

var random = new Random();
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5 + random.Next(-1, 2)); // 4-7分钟
(3)缓存数据一致性

问题:缓存数据与数据库数据不一致(如更新了图书信息,但缓存未同步)。

解决方案:

  1. 主动更新 / 删除缓存:数据修改后,立即调用_memCache.Remove(cacheKey)或重新设置缓存;
  2. 短过期时间:对一致性要求不高的场景(如博客),设置短过期时间,降低不一致窗口;
  3. 事件通知:分布式系统中,通过消息队列(如 RabbitMQ)通知所有节点更新缓存。

6. 内存缓存帮助类

直接使用IMemoryCache可能存在以下问题:

  • 重复编写过期时间、随机偏移等逻辑;
  • 缓存IQueryable/IEnumerable可能触发延迟加载(EF Core 上下文已释放,导致查询失败);
  • 未统一处理缓存穿透 / 雪崩问题。

因此需要通过存缓存帮助类进行封装。

(1)接口定义
/// <summary>
/// 内存缓存帮助类接口(封装通用逻辑)
/// </summary>
public interface IMemoryCacheHelper
{
    /// <summary>
    /// 获取/创建缓存(同步)
    /// </summary>
    /// <typeparam name="TResult">缓存类型(禁止IQueryable/IEnumerable)</typeparam>
    /// <param name="cacheKey">缓存键</param>
    /// <param name="valueFactory">缓存工厂方法</param>
    /// <param name="expireSeconds">基础过期秒数(会添加随机偏移)</param>
    /// <returns>缓存值</returns>
    TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int expireSeconds)
        where TResult : class;

    /// <summary>
    /// 获取/创建缓存(异步)
    /// </summary>
    Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int expireSeconds)
        where TResult : class;

    /// <summary>
    /// 删除缓存
    /// </summary>
    void Remove(string cacheKey);
}
(2)实现类
public class MemoryCacheHelper : IMemoryCacheHelper
{
    private readonly IMemoryCache _memoryCache;
    private readonly Random _random;

    public MemoryCacheHelper(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
        _random = new Random();
    }

    public TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int expireSeconds)
        where TResult : class
    {
        return _memoryCache.GetOrCreate(cacheKey, entry =>
        {
            // 设置随机过期时间(解决缓存雪崩)
            var randomOffset = _random.Next(-expireSeconds / 10, expireSeconds / 10 + 1); // 10%偏移
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(expireSeconds + randomOffset);
            // 禁止缓存IQueryable/IEnumerable(延迟加载风险)
            var result = valueFactory.Invoke(entry);
            if (result is IQueryable || result is IEnumerable && !(result is string))
            {
                throw new InvalidOperationException("禁止缓存IQueryable/IEnumerable类型,请先转换为数组/列表");
            }
            return result;
        });
    }

    public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int expireSeconds)
        where TResult : class
    {
        return await _memoryCache.GetOrCreateAsync(cacheKey, async entry =>
        {
            var randomOffset = _random.Next(-expireSeconds / 10, expireSeconds / 10 + 1);
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(expireSeconds + randomOffset);
            var result = await valueFactory.Invoke(entry);
            if (result is IQueryable || result is IEnumerable && !(result is string))
            {
                throw new InvalidOperationException("禁止缓存IQueryable/IEnumerable类型,请先转换为数组/列表");
            }
            return result;
        });
    }

    public void Remove(string cacheKey)
    {
        _memoryCache.Remove(cacheKey);
    }
}
(3)注册与使用
// Program.cs 注册
builder.Services.AddScoped<IMemoryCacheHelper, MemoryCacheHelper>();

// 业务层使用
public async Task<Book[]> GetAllBooksAsync()
{
    return await _cacheHelper.GetOrCreateAsync(
        "AllBooks",
        async (entry) => await _dbCtx.Books.ToArrayAsync(),
        300); // 基础过期5分钟,实际4.5-5.5分钟
}

7. 分布式缓存

内存缓存的局限:仅当前进程可用,多服务器部署时缓存不共享,且进程重启后丢失。分布式缓存将数据存储在独立的缓存服务器(如 Redis、Memcached),所有应用节点共享缓存数据,适合分布式 / 集群部署场景。

(1)主流分布式缓存对比
缓存服务器优点缺点适用场景
SQLServer部署简单、无需额外组件性能差、不适合高频访问小型项目、临时测试
Memcached纯缓存设计、性能极高集群 / 高可用弱、键长度限制(250 字节)高并发读、简单缓存场景
Redis功能丰富(缓存 / 消息 / 持久化)、高可用 / 集群成熟性能略低于 Memcached分布式系统、高可用要求高的场景
(2)Redis 分布式缓存使用(推荐)
  1. 安装 NuGet 包:
    Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
    
  2. 注册服务(Program.cs):
    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = "localhost:6379"; // Redis连接字符串(可配置密码、数据库等)
        options.InstanceName = "BookApp_"; // 缓存键前缀(避免多应用冲突)
    });
  3. 核心接口(IDistributedCache):
    方法作用
    Get/Set同步获取 / 设置缓存(字节数组)
    GetAsync/SetAsync异步获取 / 设置缓存
    RefreshAsync刷新缓存(重置滑动过期)
    RemoveAsync删除缓存

    注意:IDistributedCache默认操作byte[]类型,需手动序列化 / 反序列化(如 JSON)。

(3)分布式缓存帮助类

进行封装序列化 / 反序列化。

/// <summary>
/// 分布式缓存帮助类(自动序列化/反序列化)
/// </summary>
public interface IDistributedCacheHelper
{
    Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, Task<TResult?>> valueFactory, int expireSeconds)
        where TResult : class;

    Task RemoveAsync(string cacheKey);
}

// 实现类
public class DistributedCacheHelper : IDistributedCacheHelper
{
    private readonly IDistributedCache _distributedCache;
    private readonly Random _random;
    private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };

    public DistributedCacheHelper(IDistributedCache distributedCache)
    {
        _distributedCache = distributedCache;
        _random = new Random();
    }

    public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, Task<TResult?>> valueFactory, int expireSeconds)
        where TResult : class
    {
        // 1. 尝试获取缓存
        var cacheValue = await _distributedCache.GetStringAsync(cacheKey);
        if (!string.IsNullOrEmpty(cacheValue))
        {
            return JsonSerializer.Deserialize<TResult>(cacheValue, _jsonOptions);
        }

        // 2. 缓存未命中,执行工厂方法
        var options = new DistributedCacheEntryOptions();
        var randomOffset = _random.Next(-expireSeconds / 10, expireSeconds / 10 + 1);
        options.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(expireSeconds + randomOffset);
        
        var result = await valueFactory.Invoke(options);
        
        // 3. 存入缓存(空结果也缓存,解决穿透)
        if (result != null)
        {
            cacheValue = JsonSerializer.Serialize(result, _jsonOptions);
            await _distributedCache.SetStringAsync(cacheKey, cacheValue, options);
        }
        else
        {
            // 空结果缓存短时间(如1分钟)
            await _distributedCache.SetStringAsync(cacheKey, "", new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1)
            });
        }

        return result;
    }

    public async Task RemoveAsync(string cacheKey)
    {
        await _distributedCache.RemoveAsync(cacheKey);
    }
}

总结

  1. 依赖注入: Core 内置 DI 容器,核心注册方式为builder.Services.AddXxx,注入方式优先选择构造函数注入,[FromServices]仅用于特殊场景;Zack.Commons 通过 “反射 + 模块化” 实现服务注册的解耦,适合大型项目的模块化开发。
  2. 缓存: 缓存分类:客户端缓存(浏览器)→ 服务器端响应缓存(HTTP 响应)→ 内存缓存(进程内)→ 分布式缓存(跨进程 / 服务器),按需选择; 缓存问题:穿透(缓存空值)、雪崩(随机过期时间)、一致性(主动更新 / 短过期)是缓存使用的核心关注点; 封装缓存帮助类:统一处理过期策略、类型限制、序列化等问题,提升代码复用性和可维护性。
深入理解 ASP.NET Core 中的依赖注入:原理、实现与应用 ASP.NET Core 的依赖注入为开发者提供了强大的功能,使得应用程序更加灵活、可维护和可测试。通过 DI 容器,开发者可以轻松地管理服务的生命周期,并通过构造函数注入等方式实现解耦。理解 DI 的原理和实现方式,对于构建高质量的、易于维护的 Web 应用程序至关重要。在实际开发中,良好的依赖注入实践可以显著提升应用程序的架构和质量。 阅读详情

相关推荐

基于ASP.NET Core 3.1与Ocelot的API网关实战项目

在微服务架构中,服务数量的激增导致客户端与后端服务之间的通信复杂度显著上升。API网关作为系统的统一入口,有效解耦了客户端与微服务间的直接依赖,实现了请求路由、认证鉴权、限流熔断等横切关注点的集中管理。常见的设计模式包括:边缘网关(处理外部流量)与内部网关(管理服务间调用),采用反向代理模式实现透明转发,利用聚合器模式整合多个微服务响应以降低往返延迟,以及通过代理链模式支持多级网关级联,适用于大型分布式系统分层治理。graph LR。

weixin_42601547的博客 855

.Net Core 依赖注入

为什么要使用依赖注入框架 .Net Core DI 核心类 三种生命周期 服务注册 单例注册 作用域注册 瞬时注册 直接注入实例 工厂模式注册 注册不同实例 尝试注册 移除和替换注册 注册泛型模板 使用依赖注入注意点 实现 IDisposable 接口类型的释放 Autofac 基于名称的注入 属性注入 子容器 基于动态代理的 AOP

睡在上铺的猴 4929

ASP.NET Core 3.0中集成Autofac、Castle Windsor与AspectCore的AOP实战演示项目

ASP.NET Core 3.0 以高性能、跨平台和模块化架构为核心,采用中间件管道机制实现请求处理的灵活组合,原生支持依赖注入(DI),促进了应用各层之间的解耦。其轻量化的主机模型和基于约定的配置方式,极大提升了Web应用的可测试性与可维护性。然而,在日志、事务、缓存等横切关注点的实现上,传统编码易导致重复代码和业务逻辑污染。面向切面编程(AOP)通过运行时动态织入行为,实现了非侵入式增强,有效分离核心逻辑与辅助功能。

weixin_30820933的博客 463

深入理解 ASP.NET Core 依赖注入

对于 ASP.NET Core 的依赖注入、控制反转以及 Autofac 等一直没有搞明白,但这篇文章让我从根本上了解了尤其是依赖注入的概念以及在 ASP.NET Core 中的应用,特推荐给需要的你。 一、什么是依赖注入(Denpendency Injection) 这也是个老生常谈的问题,到底依赖注入是什么? 为什么要用它? 初学者特别容易对控制反转IOC(Iversion of Cont...

Carey 8249

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

最近有个需求就是一个抽象仓储层接口方法需要SqlServer以及Oracle两种实现方式,为了灵活我在依赖注入的时候把这两种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选择使用哪种实现呢?如果可以的话那么我只需要在配置文件中进行相应的配置即可获取到正确的实现方法的调用,这样的话岂不快哉!今天我们就来一起探讨下实现这种需求的几种实现方式吧。

【NTN 卫星通信】Starlink 星链卫星有多大?详解尺寸与技术参数

SpaceX的星链项目正在重塑全球互联网连接,尤其是在服务不足的地区。但星链卫星的实际尺寸有多大?

weixin_43603524的博客 1万+

NetCore依赖注入ioc使用三种方式

环境: net 5 vs2022 注入方式: 分别为属性、构造函数、方法特性(FromServices)使用注入 [Route("api/[controller]")] [ApiController] public class FirstController : ControllerBase { public ICalc _calc; /// <summary> /// 属性注入 /

czjnoe的博客 5890

JMeter压测Dubbo接口的完整落地方案(适配3.x)

Dubbo作为主流Java微服务RPC框架,其接口压测长期面临协议私有、注册发现复杂、序列化耦合度高等技术门槛。理解Dubbo基于TCP长连接、泛化调用与SPI扩展机制的核心原理,是实现稳定压测的前提;技术价值在于绕过HTTP网关直击服务核心链路,保障大促等关键场景的性能可信度;典型应用场景包括电商订单链路压测、风控/库存等内部Dubbo服务SLA验证,以及CI/CD中性能左移实践。本文聚焦Dubbo 3.x版本,提供零第三方插件、可复用、避坑的JMeter Java Sampler落地路径。

weixin_33700809的博客 550

.Net core服务注入

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? 示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。 二、使用步骤 1.引入库 代码

qq_36819973的博客 1368

Xilinx Chip2Chip IP核实战:Aurora PHY通信配置与仿真避坑指南

本文深入解析Xilinx Chip2Chip IP核与Aurora PHY的通信配置与仿真优化,提供实战避坑指南。通过AXI协议转换桥实现FPGA间高速数据传输,详细讲解Aurora PHY参数设置、仿真加速技巧及常见问题解决方案,帮助工程师快速部署稳定高效的芯片间通信系统。

g5h6i7j的博客 1006

【Codesys】获取从站信息,判断是否从站断联,总线丢失

获取EtherCAT从站信息,判断是否从站断联,总线丢失

qq_35316470的博客 5732

ASP.NET Core注入有几种?各有什么优缺点?

在ASP.NET Core中,依赖注入(Dependency Injection, DI)是一个非常重要的概念。ASP.NET Core支持以下三种不同的注入方式:1.构造函数注入(Constructor Injection)构造函数注入是ASP.NET Core中最常用的注入方式。该方式通过在类的构造函数中声明该类所需要的依赖项,然后ASP.NET Core运行时会自动实例化这些依赖项并传递给类...

dotNET跨平台 791

AS7341光谱传感器实战:如何用STM32F103精准测量环境色温(附完整代码)

本文详细介绍了如何使用STM32F103驱动AS7341光谱传感器实现环境色温的精准测量。从硬件连接到驱动开发,再到色温计算算法的实现,提供了完整的代码示例和优化建议,帮助开发者在智能照明、农业监测等领域快速应用AS7341进行高精度色温检测。

weixin_30352645的博客 478

Picsou Finance架构深度剖析:端口适配器模式如何优雅解耦银行与券商集成

Picsou 是一款自托管的个人财务管理面板(Self-hosted Personal Finance Dashboard),能把银行账户、券商持仓、加密货币交易所和链上钱包聚合到一处,统一跟踪余额、净值与理财目标。它的后端是 Spring Boot 应用,但要同时对接 Enable Banking(PSD2 银行聚合)、Trade Republic、Bourse Direct、Fortuneo

gitblog_00582的博客 327

具身智能:原理、算法与系统 第18章 模仿学习与人类示范

与基于试错机制的强化学习不同,模仿学习假设存在能够提供高质量轨迹示范的专家策略,学习者的目标是在不直接访问专家内部决策机制的情况下,复现或泛化专家的行为模式。此外,选项 GAIL(Option-GAIL)将分层强化学习与对抗模仿结合,学习选项(Options)级别的策略,能够捕捉专家示范中的多模态行为与高层意图。X-Sim 框架提出从真实到模拟再到真实的迁移路径:首先从人类视频中重建物理仿真环境,在仿真中训练状态级策略,随后通过策略蒸馏获得基于视觉的策略,最后通过在线领域自适应对齐真实与仿真观测。

VectorShift的博客 125

安卓手机强制清理数据恢复出厂设置后无法设置密码怎么办?(分享亲身经历的有效解决方案)

📱安卓手机强制清数据恢复出厂设置后,竟无法设置锁屏密码!本文将解析问题根源,分享亲测有效的解决方案,帮你快速摆脱困扰🎉

XiaoqiangClub的博客 242

生产级 .NET 后端开发实战指南:深入解析 agents24/dotnet-contribution 插件中的 C、ASP.NET Core 与 EF Core/Dapper 模式库

本文聚焦于 agents24/agents 仓库中 [dotnet-contribution 插件](https://link.gitcode.com/i/8e19d7c04340a14fcbe5da8569a9898a),它是面向 Claude Code、Codex、Cursor、OpenCode、GitHub Copilot、Google Antigravity 等多 harness Agen

gitblog_00062的博客 392

基于Python的农业市场价格抓取与供需分析:现代爬虫技术与实战应用

在正式开始爬虫开发之前,我们首先了解爬虫的基本概念和工作原理。爬虫(Web Scraping)是通过编程的方式,自动从互联网上获取信息的技术。它可以帮助我们提取HTML页面中的特定数据,通常用于收集并处理大量信息。发送HTTP请求:爬虫需要向目标网站发送请求,获取网站的HTML页面内容。解析HTML页面:通过HTML解析器对获取的页面内容进行解析,提取其中需要的数据。数据清洗与存储:将提取的数据进行清洗,去除多余的字符或无关内容,然后存储到合适的格式(如CSV文件、数据库等)中。数据分析与可视化。

2201_76125261的博客 1117

layui 添加提交数据格式_开源推荐:.Net Core3.1 + EF Core + LayUI 封装的MVC版后台管理系统...

ASP.NET Core是一个由微软创建的,用于构建Web应用程序,API,微服务的Web框架。它使用常见的模式,例如MVC(模型-视图-控制器),依赖注入,和一个由中间件构成它基于Apache 2.0许可开放源码,就是说,源可以自由获取,并且欢迎社区成员以缺陷修复和新功能提交的方式进行贡献。ASP.NET Core在Microsoft的.NET运行时库上,例如Java的虚拟机(JVM)...

weixin_36015463的博客 332

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Qwen3-Reranker-0.6B与.NET平台集成方案

本文介绍了如何在星图GPU平台上自动化部署Qwen3-Reranker-0.6B语义重排序服务,并将其与.NET平台集成。该方案能显著提升企业级文档检索系统的精准度,例如在智能问答场景中,可将用户查询与海量知识库文档进行深度语义匹配,从而返回最相关的结果。

weixin_42588555的博客 137

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Qwen3智能字幕对齐系统在.NET生态中的集成

本文介绍了如何在星图GPU平台上自动化部署🎬 清音刻墨 · Qwen3 智能字幕对齐系统,实现高效精准的视频字幕对齐。该系统能自动匹配音频与文本时间轴,显著提升视频制作效率,特别适用于影视剪辑、在线教育等需要快速生成同步字幕的场景。

weixin_33256096的博客 111

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

一文读懂Asp.net core 依赖注入(Dependency injection)

一、什么是依赖注入 首先在Asp.net core中是支持依赖注入软件设计模式,或者说依赖注入是asp.net core的核心; 依赖注入(DI)和控制反转(IOC)基本是一个意思,因为说起来谁都离不开谁;或者可以说他们是同一个概念的不同角度描述; 软件设计原则中有一个依赖倒置原则(DIP),就是为了解耦;高层模块不应该依赖于底层模块。二者都应该依赖于抽...

每天都有新发现 5307

ASP.NET Core MVC

MVC概述 asp.net core 中安装mvc services.AddControllersWithViews();//添加mvc控制器和视图服务 endpoints.MapControllerRoute(//终结点默认路由 依赖注入 配置服务将对象注入到容器 控制器构造时通过依赖注入从容器获取对象 控制器 视图 https://localhost:44369/Home/Details 默认路径下没有发现视图 视图发现 从控制器传递数据到视图 ...

随笔 4267

ASP.NET Core如何在ActionFilterAttribute里做依赖注入

点击蓝字关注我在ASP.NET Core里,我们可以使用构造函数注入很方便地对Controller,ViewComponent等部件做依赖注入。但是如何给过滤器Actio...

dotNET跨平台 3137

ASP.NET CORE 依赖注入的三种方式,分别是什么,使用场景

在(Dependency Injection,简称 DI)中,通常有三种常见的服务生命周期模式,用于控制服务实例的创建和管理。和。这三种模式在Core 中非常重要,因为它们决定了依赖项在应用程序中的生命周期。

情绪不稳定的98年程序员 1713

ASP.NET Core依赖注入&AutoFac

1. 前言 关于IOC模式和DI技术,网上已经有很多相关的探讨,再次就不过多赘述了,只是简单介绍一下它们的概念 控制反转(IoC/Inverse Of Control): 调用者将创建实例的控制权交给IOC容器,由容器创建,所以称为控制反转。 依赖注入(DI/Dependence injection) : 容器创建好实例后再注入给调用者的过程称为依赖注入。依赖注入技术让...

dhmrcnaus821038627的博客 343
上一篇: 【ASP.NET CORE】 2. 前后端分离开发
下一篇: 【ASP.NET CORE】 4. 集成配置系统、分层架构
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值