当前位置: 首页 > news >正文

【EFCore仓储模式】介绍一个EFCore的Repository实现

阅读本文你的收获

  1. 了解仓储模式及泛型仓储的优点
  2. 学会封装泛型仓储的一般设计思路
  3. 学习在ASP.NET Core WebAPI项目中使用EntityFrameworkCore.Data.Repository

本文中的案例是微软EntityFrameworkCore的一个仓储模式实现,这个仓储库不是我自己写的,而是使用了一个老外写的EntityFrameworkCore.Data.Repository,大家可以一起来学习一下,如果你觉得合适,可以直接用在你的项目中,很方便。案例代码下载

一、 什么是仓储模式

仓储(Repository)模式自2004年首次作为领域驱动模型DDD设计的一部分引入,仓储本质上是提供数据的抽象,以便应用程序可以使用具有接口的相似的简单抽象集合。从此集合中CURD是通过一系列直接的方法完成,无需处理连接、命令等问题,使用此种模式可帮助实现松耦合,并保持领域对象的持久性无知。

  • 仓储模式是为了在程序的数据访问层和业务逻辑层之间创建的一个抽象层
  • 仓储模式是一种数据访问模式,提供一种更松散耦合的数据访问方法
  • 将创建数据访问的逻辑写在单独的类中即仓储
  • 仓储负责和业务层进行持久化通信

下图为控制器和仓储协同工作的图例:
仓储示意图

二、泛型仓储

仓储(Repository)是存在于工作单元和数据库之间单独分离出来的一层,是对数据访问的封装。其优点是

  • 业务层无需知道具体实现,达到分离关注点;
  • 提高对数据库访问的维护,对于仓储的改变,并不改变业务的逻辑;

如果我们采用的ORM框架是EF Core,实现仓储模式的话,那么类图设计一般如下:
仓储模式类图通常实现仓储的时候,会使用泛型技术封装增删改查的的通用功能,类型T即为具体要进行增删改查处理的实体类型。

使用泛型仓储(Generic Repository)的好处有以下几点:

  • 更好的可重用性:泛型仓储可以在多个实体类型之间共享和重用,减少了重复的代码编写和维护工作。
  • 更好的类型安全性:使用泛型仓储可以在编译时就约束数据的类型,减少了运行时类型错误的可能性。
  • 更好的简洁性:泛型仓储可以通过使用通用的方法和接口来简化数据访问的逻辑,提供统一的CRUD(增删改查)操作接口。
  • 更好的可测试性:泛型仓储使得数据访问逻辑可以更容易地进行单元测试,因为可以使用模拟或者假数据来代替实际的数据存储。
  • 更好的扩展性:泛型仓储可以通过继承或者接口实现来扩展其功能,例如添加自定义的查询方法或者过滤器。

三、基于EF Core实现的泛型仓储案例

开发环境:

操作系统: Windows 10 专业版
平台版本是:.NET 6
开发框架:ASP.NET Core WebApi、Entity Framework Core
开发工具:Visual Studio 2022
数据库: MySQL 5.7+

安装NuGet包

使用的NuGet包主要有:

  1. EntityFrameworkCore.Data.Repository – 一个老外封装好的开源EfCore仓储实现
  2. EntityFrameworkCore.Data.UnitOfWork-- 跟以上配套的工作单元
  3. Pomelo.EntityFrameworkCore.MySql – MySQL数据库提供程序

简单剖析一下 EntityFrameworkCore.Data.Repository

可以看到,作者定义了泛型仓储接口如下:

public interface IRepository<T> : IRepository, IDisposable, ISyncRepository<T>, ISyncRepository, IQueryFactory<T>, IAsyncRepository<T>, IAsyncRepository where T : class
{
}

IRepository< T >接口分别集继承了 ISyncRepository< T > 和 IAsyncRepository< T >这两个接口,分别是同步仓储方法接口和异步仓储方法接口。

//同步仓储方法接口
public interface ISyncRepository<T> : ISyncRepository, IRepository, IDisposable, IQueryFactory<T> where T : class
{IList<T> Search(IQuery<T> query);IList<TResult> Search<TResult>(IQuery<T, TResult> query);T SingleOrDefault(IQuery<T> query);TResult SingleOrDefault<TResult>(IQuery<T, TResult> query);T FirstOrDefault(IQuery<T> query);TResult FirstOrDefault<TResult>(IQuery<T, TResult> query);T LastOrDefault(IQuery<T> query);TResult LastOrDefault<TResult>(IQuery<T, TResult> query);bool Any(Expression<Func<T, bool>> predicate = null);int Count(Expression<Func<T, bool>> predicate = null);long LongCount(Expression<Func<T, bool>> predicate = null);TResult Max<TResult>(Expression<Func<T, TResult>> selector, Expression<Func<T, bool>> predicate = null);TResult Min<TResult>(Expression<Func<T, TResult>> selector, Expression<Func<T, bool>> predicate = null);decimal Average(Expression<Func<T, decimal>> selector, Expression<Func<T, bool>> predicate = null);decimal Sum(Expression<Func<T, decimal>> selector, Expression<Func<T, bool>> predicate = null);T Attach(T entity);void AttachRange(IEnumerable<T> entities);T Add(T entity);void AddRange(IEnumerable<T> entities);T Update(T entity, params Expression<Func<T, object>>[] properties);int Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> expression);void UpdateRange(IEnumerable<T> entities, params Expression<Func<T, object>>[] properties);T Remove(T entity);int Remove(Expression<Func<T, bool>> predicate);void RemoveRange(IEnumerable<T> entities);int ExecuteSqlCommand(string sql, params object[] parameters);IList<T> FromSql(string sql, params object[] parameters);void ChangeTable(string table);void ChangeState(T entity, EntityState state);EntityState GetState(T entity);void Reload(T entity);void TrackGraph(T rootEntity, Action<EntityEntryGraphNode> callback);void TrackGraph<TState>(T rootEntity, TState state, Func<EntityEntryGraphNode<TState>, bool> callback);IQueryable<T> ToQueryable(IQuery<T> query);IQueryable<TResult> ToQueryable<TResult>(IQuery<T, TResult> query);
}
//异步仓储方法接口
public interface IAsyncRepository<T> : IAsyncRepository, IRepository, IDisposable, IQueryFactory<T> where T : class
{Task<IList<T>> SearchAsync(IQuery<T> query, CancellationToken cancellationToken = default(CancellationToken));Task<IList<TResult>> SearchAsync<TResult>(IQuery<T, TResult> query, CancellationToken cancellationToken = default(CancellationToken));Task<T> SingleOrDefaultAsync(IQuery<T> query, CancellationToken cancellationToken = default(CancellationToken));Task<TResult> SingleOrDefaultAsync<TResult>(IQuery<T, TResult> query, CancellationToken cancellationToken = default(CancellationToken));Task<T> FirstOrDefaultAsync(IQuery<T> query, CancellationToken cancellationToken = default(CancellationToken));Task<TResult> FirstOrDefaultAsync<TResult>(IQuery<T, TResult> query, CancellationToken cancellationToken = default(CancellationToken));Task<T> LastOrDefaultAsync(IQuery<T> query, CancellationToken cancellationToken = default(CancellationToken));Task<TResult> LastOrDefaultAsync<TResult>(IQuery<T, TResult> query, CancellationToken cancellationToken = default(CancellationToken));Task<bool> AnyAsync(Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<int> CountAsync(Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<long> LongCountAsync(Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<TResult> MaxAsync<TResult>(Expression<Func<T, TResult>> selector, Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<TResult> MinAsync<TResult>(Expression<Func<T, TResult>> selector, Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<decimal> AverageAsync(Expression<Func<T, decimal>> selector, Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<decimal> SumAsync(Expression<Func<T, decimal>> selector, Expression<Func<T, bool>> predicate = null, CancellationToken cancellationToken = default(CancellationToken));Task<T> AddAsync(T entity, CancellationToken cancellationToken = default(CancellationToken));Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default(CancellationToken));Task<int> UpdateAsync(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> expression, CancellationToken cancellationToken = default(CancellationToken));Task<int> RemoveAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default(CancellationToken));Task<IList<T>> FromSqlAsync(string sql, IEnumerable<object> parameters = null, CancellationToken cancellationToken = default(CancellationToken));Task<int> ExecuteSqlCommandAsync(string sql, IEnumerable<object> parameters = null, CancellationToken cancellationToken = default(CancellationToken));Task ReloadAsync(T entity, CancellationToken cancellationToken = default(CancellationToken));
}
//泛型仓储的实现(列举一部分实现,感兴趣的可以自己查看源码)
public class Repository<T> : IRepository<T>, IRepository, IDisposable, ISyncRepository<T>, ISyncRepository, IQueryFactory<T>, IAsyncRepository<T>, IAsyncRepository where T : class
{private bool _disposed;protected DbContext DbContext { get; }protected DbSet<T> DbSet { get; }public Repository(DbContext dbContext){DbContext = dbContext ?? throw new ArgumentNullException("dbContext", "dbContext cannot be null.");DbSet = dbContext.Set<T>();}public virtual ISingleResultQuery<T> SingleResultQuery(){return EntityFrameworkCore.QueryBuilder.SingleResultQuery<T>.New();}public virtual IMultipleResultQuery<T> MultipleResultQuery(){return EntityFrameworkCore.QueryBuilder.MultipleResultQuery<T>.New();}public virtual ISingleResultQuery<T, TResult> SingleResultQuery<TResult>(){return SingleResultQuery<T, TResult>.New();}public virtual IMultipleResultQuery<T, TResult> MultipleResultQuery<TResult>(){return MultipleResultQuery<T, TResult>.New();}public virtual IList<T> Search(IQuery<T> query){if (query == null){throw new ArgumentNullException("query", "query cannot be null.");}return ToQueryable(query).ToList();}public virtual T Add(T entity){if (entity == null){throw new ArgumentNullException("entity", "entity cannot be null.");}DbSet.Add(entity);return entity;}public virtual void AddRange(IEnumerable<T> entities){if (entities == null){throw new ArgumentNullException("entities", "entities cannot be null.");}if (entities.Any()){DbSet.AddRange(entities);}}public virtual T Update(T entity, params Expression<Func<T, object>>[] properties){if (entity == null){throw new ArgumentNullException("entity", "entity cannot be null.");}if (properties != null && properties.Any()){EntityEntry<T> entityEntry = DbContext.Entry(entity);foreach (Expression<Func<T, object>> propertyExpression in properties){PropertyEntry propertyEntry;try{propertyEntry = entityEntry.Property(propertyExpression);}catch{propertyEntry = null;}if (propertyEntry != null){propertyEntry.IsModified = true;continue;}ReferenceEntry referenceEntry;try{referenceEntry = entityEntry.Reference(propertyExpression);}catch{referenceEntry = null;}if (referenceEntry != null){EntityEntry targetEntry = referenceEntry.TargetEntry;DbContext.Update(targetEntry.Entity);}}}else{DbSet.Update(entity);}return entity;}public virtual int Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> expression){if (predicate == null){throw new ArgumentNullException("predicate", "predicate cannot be null.");}if (expression == null){throw new ArgumentNullException("expression", "expression cannot be null.");}return Queryable.Where(DbSet, predicate).Update(expression);}
public virtual T Remove(T entity){if (entity == null){throw new ArgumentNullException("entity", "entity cannot be null.");}DbSet.Remove(entity);return entity;}public virtual int Remove(Expression<Func<T, bool>> predicate){if (predicate == null){throw new ArgumentNullException("predicate", "predicate cannot be null.");}return Queryable.Where(DbSet, predicate).Delete();}public virtual void RemoveRange(IEnumerable<T> entities){if (entities == null){throw new ArgumentNullException("entities", "entities cannot be null.");}if (entities.Any()){DbSet.RemoveRange(entities);}}

四、 在WebApi项目中使用EntityFrameworkCore.Data.Repository

  1. 在appsettings.json中配置连接字符串
//配置连接字符串"ConnectionStrings": {"default": "Server=localhost;Database=20240114WebApplication;user=root;password=12345;port=3306"},
  1. 在Program.cs中注册相关服务
//注册DbContext服务
string connectionString = builder.Configuration.GetConnectionString("default");
builder.Services.AddDbContext<MyDbContext>(option => option.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)
));
builder.Services.AddScoped<DbContext, MyDbContext>();// 注册工作单元
builder.Services.AddUnitOfWork();
//builder.Services.AddUnitOfWork<MyDbContext>(); // 多数据库支持
//注册泛型仓储服务
builder.Services.AddScoped(typeof(Repository<>));
  1. 设计实体类,本例图书管理为例
 public class Book{public Book(){Title = string.Empty;ISBN = string.Empty;}[Key]public long Id { get; set; }[Required][MaxLength(100)]public string Title { get; set; }[Required][MaxLength(20)]public string ISBN { get; set; }public long CategoryId { get; set; }//导航属性[ForeignKey("CategoryId")]public virtual Category Category { get; set; }}public class Category
{public Category(){Name = string.Empty;Code = string.Empty;}[Key]public long Id { get; set; }/// <summary>/// 分类代码/// </summary>[Required][MaxLength(30)]public string Code { get; set; }/// <summary>/// 分类名/// </summary>[Required][MaxLength(30)]public string Name { get; set; }//导航属性public virtual IList<Book> Books { get; set; }
}
  1. 实现图书管理API接口

(1)依赖注入泛型仓储和工作单元对象:

[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{//泛型仓储private readonly Repository<Book> _bookRepository;private readonly Repository<Category> _categoryRepository;//工作单元private readonly IUnitOfWork _unitOfWork;//构造方法public BooksController(Repository<Book> bookRepository,Repository<Category> categoryRepository,IUnitOfWork unitOfWork){_bookRepository = bookRepository;_categoryRepository = categoryRepository;_unitOfWork = unitOfWork;}
}

(2)实现分页显示:

  //分页查询(使用Include加载导航属性)[HttpGet("GetPageList")]public async Task<ActionResult<IPagedList<BookOutput>>> GetPageList([FromQuery] BookPageRequestInput input){//创建查询对象-MultipleResultQuery表示多结果集查询var query = _bookRepository.MultipleResultQuery<BookOutput>().Page(input.PageIndex, input.PageSize) //分页.AndFilter(b => string.IsNullOrEmpty(input.Title) || b.Title.StartsWith(input.Title)) //筛选条件.Include(q => q.Include(x => x.Category))  //级联加载.OrderByDescending("Title").ThenBy("ISBN") //排序.Select(b => new BookOutput                //投影{CategoryId = b.CategoryId,CategoryCode = b.Category.Code,CategoryName = b.Category.Name,ISBN = b.ISBN,Title = b.Title,Id = b.Id}) as IMultipleResultQuery<Book, BookOutput>; //转换类型//执行查询var result = (await _bookRepository.SearchAsync(query)).ToPagedList(query.Paging.PageIndex,query.Paging.PageSize,query.Paging.TotalCount);return Ok(result);}
  //分页查询(使用IQueryable.Join方法进行联表查询)[HttpGet("GetBookPage")]public async Task<ActionResult<PagedList<BookOutput>>> GetBookPage([FromQuery] BookPageRequestInput input){//获取可IQueryable可查询对象var books = _bookRepository.ToQueryable(_bookRepository.MultipleResultQuery());var categories = _categoryRepository.ToQueryable(_categoryRepository.MultipleResultQuery());var query = books.Join(categories, b => b.CategoryId, c => c.Id,(b, c) => new BookOutput{CategoryId = b.CategoryId,CategoryCode = b.Category.Code,CategoryName = b.Category.Name,ISBN = b.ISBN,Title = b.Title,Id = b.Id}).Where(b => string.IsNullOrEmpty(input.Title) || b.Title.StartsWith(input.Title)).OrderBy(b => b.Id);PagedList<BookOutput> result = new PagedList<BookOutput>();result.TotalCount = await query.CountAsync();result.Items = await query.Skip((input.PageIndex - 1) * input.PageSize).Take(input.PageSize).ToListAsync();return result;}

(3)添加图书:

  //POST api/Books[HttpPost]public async Task<ActionResult<int>> Add([FromBody] BookAddOrUpdateInput input){Book book = new Book{CategoryId = input.CategoryId,ISBN = input.ISBN,Title = input.Title};await _bookRepository.AddAsync(book);var result = await _unitOfWork.SaveChangesAsync();return result;}

(4)修改图书:

   //PUT api/Books[HttpPut]public async Task<ActionResult<int>> Update([FromBody] BookAddOrUpdateInput input){var numAffected = await _bookRepository.UpdateAsync(b => b.Id == input.Id, b => new Book{CategoryId = input.CategoryId,ISBN = input.ISBN,Title = input.Title});var result = await _unitOfWork.SaveChangesAsync();return result;}

(5)删除图书:

   //DELETE api/Books/{id}[HttpDelete("{id}")]public async Task<ActionResult<int>> Delete(long id){var numAffected = await _bookRepository.RemoveAsync(b => b.Id == id);var result = await _unitOfWork.SaveChangesAsync();return result;}

(6)根据ID获取图书:

  //GET api/Books/{id}[HttpGet("{id}")]public async Task<ActionResult<BookOutput>> Get(long id){//创建查询对象,SingleResultQuery表示单条结果查询var query = _bookRepository.SingleResultQuery<BookOutput>().Include(q => q.Include(x => x.Category)).AndFilter(b => b.Id == id).Select(b => new BookOutput  //投影{CategoryId = b.CategoryId,CategoryCode = b.Category.Code,CategoryName = b.Category.Name,ISBN = b.ISBN,Title = b.Title,Id = b.Id});return await _bookRepository.SingleOrDefaultAsync(query);}

本次演示了在ASP.NET Core中使用泛型仓储模式封装EF Core的CRUD方法,推荐大家可以尝试一下EntityFrameworkCore.Data.Repository这个开源仓储实现类。如果本文对你有帮助的话,请点赞+评论+关注,或者转发给需要的朋友。

相关文章:

【EFCore仓储模式】介绍一个EFCore的Repository实现

阅读本文你的收获 了解仓储模式及泛型仓储的优点学会封装泛型仓储的一般设计思路学习在ASP.NET Core WebAPI项目中使用EntityFrameworkCore.Data.Repository 本文中的案例是微软EntityFrameworkCore的一个仓储模式实现&#xff0c;这个仓储库不是我自己写的&#xff0c;而是使…...

oracle篇—19c新特性自动索引介绍

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…...

稳定性——JE流程

1. RuntimeInit.commonInit() 上层应用都是由Zygote fork孵化出来的&#xff0c;分为system_server进程和普通应用进程进程创建之初会设置未捕获异常的处理器&#xff0c;当系统抛出未捕获的异常时候都会交给异常处理器RuntimeInit.java的commonInit方法设置UncaughtHandler …...

【控制篇 / 分流】(7.4) ❀ 03. 对国内和国际IP网段访问进行分流 ❀ FortiGate 防火墙

【简介】公司有两条宽带用来上网&#xff0c;一条电信&#xff0c;一条IPLS国际专线&#xff0c;由于IPLS仅有2M&#xff0c;且价格昂贵&#xff0c;领导要求&#xff0c;访问国内IP走电信&#xff0c;国际IP走IPLS&#xff0c;那么应该怎么做&#xff1f; 国内IP地址组 我们已…...

01-开始Rust之旅

上一篇&#xff1a;00-Rust前言 1. 下载Rust 官方推荐使用 rustup 下载 Rust&#xff0c;这是一个管理 Rust 版本和相关工具的命令行工具。下载时需要连接互联网。 这边提供了离线安装版本。本人学习的机器环境为&#xff1a; ubuntu x86_64&#xff0c;因此选用第②个工具链&a…...

华南理工大学数字信号处理实验实验一(薛y老师版本)matlab源码

一、实验目的 1、加深对离散信号频谱分析的理解&#xff1b; 2、分析不同加窗长度对信号频谱的影响&#xff1b; 3、理解频率分辨率的概念&#xff0c;并分析其对频谱的 影响&#xff1b; 4、窗长和补零对DFT的影响 实验源码&#xff1a; 第一题&#xff1a; % 定义离散信…...

一篇文章看懂云渲染,云渲染是什么?云渲染如何计费?云渲染怎么选择

云渲染是近年兴起的新行业&#xff0c;很多初学者对它不是很了解&#xff0c;云渲染是什么&#xff1f;为什么要选择云渲染&#xff1f;它是如何计费的又怎么选择&#xff1f;这篇文章我们就带大家了解下吧。 云渲染是什么 云渲染简单来说就是把本地的渲染工作迁移到云端进行的…...

C++进阶--哈希表模拟实现unordered_set和unordered_map

哈希表模拟实现unordered_set和unordered_map 一、定义哈希表的结点结构二、定义哈希表的迭代器三、定义哈希表的结构3.1 begin()和end()的实现3.2 默认成员函数的实现3.2.1 构造函数的实现3.2.2 拷贝构造函数的实现&#xff08;深拷贝&#xff09;3.2.3 赋值运算符重载函数的实…...

Elasticsearch各种高级文档操作

本文来记录下Elasticsearch各种文档操作 文章目录 初始化文档数据查询所有文档匹配查询文档关键字精确查询文档多关键字精确查询文档字段匹配查询文档指定查询字段查询文档过滤字段查询文档概述指定想要显示的字段示例指定不想要显示的字段示例 组合查询文档范围查询文档概述使…...

激光无人机打击系统——光束控制和指向系统

激光无人机&#xff08;UAV&#xff09;打击系统中的光束控制和指向系统通常包括以下几个关键组件和技术&#xff1a; 激光发射器&#xff1a;这是系统的核心&#xff0c;负责生成高能量的激光束。常用的激光类型包括固体激光器、化学激光器、光纤激光器等&#xff0c;选择取决…...

pycharm import torch

目录 1 安装 2 conda环境配置 3 测试 开始学习Pytorch! 1 安装 我的电脑 Windows 11 Python 3.11 Anaconda3-2023.09-0-Windows-x86_64.exe cuda_11.8.0_522.06_windows.exe pytorch &#xff08;管理员命令行安装&#xff09; pycharm-community-2023.3.2.exe 2 c…...

flask 与小程序 购物车删除和编辑库存功能

编辑 &#xff1a; 数量加减 价格汇总 数据清空 mina/pages/cart/index.wxml <!--index.wxml--> <view class"container"><view class"title-box" wx:if"{{ !list.length }}">购物车空空如也&#xff5e;</view>…...

蓝桥杯真题(Python)每日练Day3

题目 题目分析 为了找到满足条件的放置方法&#xff0c;可以带入总盘数为2和3的情景&#xff0c;用递归做法实现。 2. A中存在1 2两个盘&#xff0c;为了实现最少次数放入C且上小下大&#xff0c;先将1放入B&#xff0c;再将2放入C&#xff0c;最后将1放入C即可。同理当A中存在…...

结构体大揭秘:代码中的时尚之选(上)

目录 结构结构的声明结构成员的类型结构体变量的定义和初始化结构体成员的访问结构体传参 结构 结构是一些值的集合&#xff0c;这些值被称为成员变量。之前说过数组是相同类型元素的集合。结构的每个成员可以是不同类型的变量&#xff0c;当然也可以是相同类型的。 我们在生活…...

【unity学习笔记】语音驱动blendershape

1.导入插件 https://assetstore.unity.com/packages/tools/animation/salsa-lipsync-suite-148442 1.选择小人&#xff0c;点击添加组件 分别加入组件&#xff1a; SALSA EmoteR Eyes Queue Processor&#xff08;必须加此脚本&#xff09;&#xff1a;控制前三个组件的脚本。…...

docker常用基础命令

文章目录 1、Docker 环境信息命令1.1、docker info1.2、docker version 2、系统日志信息常用命令2.1、docker events2.2、docker logs2.3、docker history 3、容器的生命周期管理命令3.1、docker create3.2、docker run 总结 1、Docker 环境信息命令 1.1、docker info 显示 D…...

自动驾驶中的坐标系

自动驾驶中的坐标系 自动驾驶中的坐标系 0.引言1.相机传感器坐标系2.激光雷达坐标系3.车体坐标系4.世界坐标系4.1.地理坐标系4.2.投影坐标系4.2.1.投影方式4.2.2.墨卡托(Mercator)投影4.2.3.高斯-克吕格(Gauss-Kruger)投影4.2.4.通用横轴墨卡托UTM&#xff08;UniversalTransve…...

js数组的截取和合并

在JavaScript中&#xff0c;你可以使用slice()方法来截取数组&#xff0c;使用concat()方法来合并数组。 截取数组 slice()方法返回一个新的数组对象&#xff0c;这个对象是一个由原数组的一部分浅复制而来。它接受两个参数&#xff0c;第一个参数是开始截取的位置&#xff08…...

2024美赛数学建模思路 - 案例:感知机原理剖析及实现

文章目录 1 感知机的直观理解2 感知机的数学角度3 代码实现 4 建模资料 # 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法&#xff0c;其…...

大中台,小前台:打造快速响应市场的企业竞争力

2015年&#xff0c;大家都听过“大中台、小前台”战略&#xff0c;听上去很牛。“大中台、小前台”背后完成了一件事情&#xff1a;把阿里巴巴和支付宝所有的基础技术全部统一到阿里云上&#xff0c;这是个重大的技术变革。为了完成这个技术变革&#xff0c;阿里巴巴做了非常好…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...