基于SqlSugar的开发框架循序渐进介绍- 基于MongoDB的数据库操作整合

SqlSugar的开发框架本身主要是基于常规关系型数据库设计的框架,支持多种数据库类型的接入,如SqlServer、MySQL、Oracle、PostgreSQL、SQLite等数据库,非关系型数据库的MongoDB数据库也可以作为扩展整合到开发框架里面,通过基类的继承关系很好的封装了相关的基础操作功能,极大的减少相关处理MongoDB的代码,并提供很好的开发效率。本篇随笔介绍如何在SqlSugar的开发框架整合MongoDB数据库的开发。

1、MongDB的简单介绍

MongoDB是一款由C++编写的高性能、开源、无模式的常用非关系型数据库产品,是非关系数据库当中功能最丰富、最像关系数据库的数据库。它扩展了关系型数据库的众多功能,例如:辅助索引、范围查询、排序等。 

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似Json的Bson格式,因此可以存储比较复杂的数据类型。
MongoDB 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。并且MongoDB-4.2版本开始已经支持分布式事务功能。

MongoDB数据库有几个简单的概念需要了解一下。

    1)MongoDB中的 database 有着和我们熟知的"数据库"一样的概念 (对 Oracle 来说就是 schema)。一个 MongoDB 实例中,可以有零个或多个数据库,每个都作为一个高等容器,用于存储数据。

    2)数据库中可以有零个或多个 collections (集合)。集合和传统意义上的 table 基本一致,可以简单的把两者看成是一样的东西。

    3)集合是由零个或多个 documents (文档)组成。同样,一个文档可以看成是一 row

    4)文档是由零个或多个 fields (字段)组成。,对应的就是关系数据库的 columns

    5)Indexes (索引)在 MongoDB 中扮演着和它们在 RDBMS 中一样的角色,都是为了提高查询的效率。

    6)Cursors (游标)和上面的五个概念都不一样,但是它非常重要,并且经常被忽视,其中最重要的你要理解的一点是,游标是当你问 MongoDB 拿数据的时候,它会给你返回一个结果集的指针而不是真正的数据,这个指针我们叫它游标,我们可以拿游标做我们想做的任何事情,比如说计数或者跨行之类的,而无需把真正的数据拖下来,在真正的数据上操作。

 它们的对比关系图如下所示。

数据在Mongodb里面都是以Json格式方式进行存储的,如下所示是其中的一个记录内容。

BSON格式

Bson是一种类Json的一式的存储格式,简称Binary Json,它和Json一样,支持内嵌的文档对象和数组对象,但是Bson有Json没有的一些数据类型,如Date和BinData类型。

Bson可以做为网络数据交换的一种存储形式,这个有点类似于Google的Protocol ,但是Bson是一种schema-less的存储形式,它的优点是灵活性高,但它的缺点是空间利用率不是很理想,Bson有三个特点:轻量性、可遍历性、高效性,

{“hello":"world"} 这是一个Bson的例子,其中"hello"是key name,它一般是cstring类型,字节表示是cstring::= (byte*) "/x00" ,其中*表示零个或多个byte字节,/x00表示结束符;后面的"world"是value值,它的类型一般是string,double,array,binarydata等类型。

MongDB数据库本身支持多种开发语言的驱动,MongoDB有官方的驱动如下:

我们框架基于C#开发,使用的时候,安装MongoDB的C#的驱动 MongoDB.Driver 即可。

在MongoDB数据库的集合里面,都要求文档有一个_id字段,这个是强制性的,而且这个字段的存储类型为ObjectId类型,这个值考虑了分布式的因素,综合了机器码,进程,时间戳等等方面的内容,它的构造如下所示。

ObjectId是一个12字节的  BSON 类型字符串。按照字节顺序,依次代表:

  • 4字节:UNIX时间戳
  • 3字节:表示运行MongoDB的机器
  • 2字节:表示生成此_id的进程
  • 3字节:由一个随机数开始的计数器生成的值

实体基类一般包含了一个属性Id,这个是一个字符串型的对象(也可以使用ObjectId类型,但是为了方便,我们使用字符型,并声明为ObjectId类型即可),由于我们声明了该属性对象为ObjectId类型,那么我们就可以在C#代码里面使用字符串的ID类型了。

2、基于MongoDB数据库的封装处理

以前介绍过,针对常规关系型数据库的开发,在SqlSugar开发框架上,我们设计一些基类,以便重用相关的逻辑代码,通过泛型的约束,可以提供强类型的数据接口,非常方便。

 

其中MyCrudService里面封装了很多CRUD以及常用的处理方法。类似的处理方式,我们专门为MongoDB数据库的访问操作,设计了一个功能强大的基类即可。

在数据库表的实体对应关系上,我们依旧遵循则相应的设计规则,基类实体采用IEntity<string>的接口类型,因此他们具有一个字符串的Id类型。其他业务对象继承该基类对象即可。

复制代码

    /// <summary>
    /// 基于MongoDB的实体类基类
    /// </summary>
    public class BaseMongoEntity : Entity<string>
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public override string Id { get; set; }
    }

复制代码

相应的,我们根据常规数据库的基类接口名称,在处理MongoDB数据库的操作接口的时候,名称保持一致性。

其中TEntity为强类型实体类型,而TGetListInput 是定义的一个分页接口。定义的基类接口代码如下所示。

其中接口对象 CurrentApiUser是我们用户上下文的信息,包含一些驻留在ClainPrincipal中的信息,用于记录访问接口的用户信息的。

其他接口定义类似的处理即可。

基类接口的实现类,就是我们需要设计的MongoDB数据库操作类了,初始化类的代码如下所示。

复制代码

    /// <summary>
    /// MongoDB基础仓储实现
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public abstract class BaseMongoService<TEntity, TGetListInput> : IBaseMongoService<TEntity, TGetListInput> 
        where TEntity : class, IEntity<string>, new()
        where TGetListInput : IPagedAndSortedResultRequest
    {
        protected readonly IMongoDBContext mongoContext = NullMongoDBContext.Instance;//空实现
        protected IMongoCollection<TEntity> collection;           //强类型对象集合
        protected IMongoCollection<BsonDocument> bsonCollection; //弱类型集合BsonDocument集合
        /// <summary>
        /// 当前Api用户信息
        /// </summary>
        public IApiUserSession CurrentApiUser { get; set; } = NullApiUserSession.Instance;//空实现

        /// <summary>
        /// 构造函数
        /// </summary>
        protected BaseMongoService()
        {
            //如果SerivcePovider已经设置值,则获得注入的接口对象
            if (ServiceLocator.SerivcePovider != null)
            {
                CurrentApiUser = ServiceLocator.GetService<IApiUserSession>();
                mongoContext = ServiceLocator.GetService<IMongoDBContext>();
                collection = mongoContext.GetCollection<TEntity>(typeof(TEntity).Name);//强类型对象集合
                bsonCollection = mongoContext.GetCollection<BsonDocument>(typeof(TEntity).Name);//弱类型集合BsonDocument集合
            }
        }
        /// <summary>
        /// 获取所有记录
        /// </summary>
        /// <returns></returns>
        public virtual async Task<ListResultDto<TEntity>> GetAllAsync()
        {
            var all = await collection.FindAsync(Builders<TEntity>.Filter.Empty);
            var list = await all.ToListAsync();
            return new ListResultDto<TEntity>()
            {
                Items = list
            };
        }

复制代码

我们通过构建对应的强类型Collection和弱类型Collection,来操作实体类和BsonDocument的相关操作的。其中的上下文对象,参考随笔《NoSQL – MongoDB Repository Implementation in .NET Core with Unit Testing example》进行的处理。

复制代码

   /// <summary>
    /// MongoDB 上下文对象
    /// </summary>
    public class MongoDBContext : IMongoDBContext
    {
        private IMongoDatabase _db { get; set; }
        private MongoClient _mongoClient { get; set; }
        public IClientSessionHandle Session { get; set; }

        public MongoDBContext(IOptions<Mongosettings> configuration)
        {
            _mongoClient = new MongoClient(configuration.Value.Connection);
            _db = _mongoClient.GetDatabase(configuration.Value.DatabaseName);
        }

        /// <summary>
        /// 获取强类型集合对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public IMongoCollection<T> GetCollection<T>(string name) where T : class, new()
        {
            return _db.GetCollection<T>(name);
        }
    }

    public interface IMongoDBContext
    {
        IMongoCollection<T> GetCollection<T>(string name) where T : class, new();
    }

复制代码

通过IOptions 方式我们注入对应的MongoDB数据库配置信息,在appsettings.json中添加根节点内容。

  "MongoSettings": {
    "Connection": "mongodb://localhost:27017/", //MongoDB连接字符串
    "DatabaseName": "iqidi" //MongoDB数据库名称
  },

我们在启动Web API的时候,在Program.cs 代码中配置好就可以了。

//MongoDB配置
builder.Services.Configure<Mongosettings>(builder.Configuration.GetSection("MongoSettings"));

默认初始化的IMongoDBContext是一个空接口,我们可以在Web API启动的时候,指定一个具体的实现就可以了

//添加IMongoContext实现类
builder.Services.AddSingleton<IMongoDBContext, MongoDBContext>();

对于基类接口,分页查询获取对应列表数据,是常规的处理方式,默认需要排序、分页,返回对应的数据结构,如下代码所示。

复制代码

        /// <summary>
        /// 根据条件获取列表
        /// </summary>
        /// <param name="input">分页查询条件</param>
        /// <returns></returns>
        public virtual async Task<PagedResultDto<TEntity>> GetListAsync(TGetListInput input)
        {
            var query = CreateFilteredQueryAsync(input);
            var totalCount = await query.CountAsync();
            
            //排序处理
            query = ApplySorting(query, input);
            //分页处理
            query = ApplyPaging(query, input);
            //获取列表
            var list = await query.ToListAsync();

            return new PagedResultDto<TEntity>(
               totalCount,
               list
           );
        }

复制代码

其中PagedResultDto 是我们SqlSugar开发框架参照ABP框架定义一个数据结构,包含一个TotalCount数量和一个Items的对象集合。而其中 CreateFilteredQueryAsync 是定义的一个可供业务子类重写的函数,用来处理具体的查询条件。在基类BaseMongoService中只是提供一个默认的可查询对象。

复制代码

        /// <summary>
        /// 留给子类实现过滤条件的处理
        /// </summary>
        /// <returns></returns>
        protected virtual IMongoQueryable<TEntity> CreateFilteredQueryAsync(TGetListInput input)
        {
            return collection.AsQueryable();
        }

复制代码

例如,对于一个具体的业务对象操作类,CustomerService的定义如下所示,并且具体化查询的条件处理,如下代码所示。

复制代码

namespace SugarProject.Core.MongoDB
{
    /// <summary>
    /// 基于MongoDB数据库的应用层服务接口实现
    /// </summary>
    public class CustomerService : BaseMongoService<CustomerInfo, CustomerPagedDto>, ICustomerService
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public CustomerService()
        {
        }
        /// <summary>
        /// 自定义条件处理
        /// </summary>
        /// <param name="input">查询条件Dto</param>
        /// <returns></returns>
        protected override IMongoQueryable<CustomerInfo> CreateFilteredQueryAsync(CustomerPagedDto input)
        {
            var query = base.CreateFilteredQueryAsync(input);

            query = query
                .Where(t=> !input.ExcludeId.IsNullOrWhiteSpace() && t.Id != input.ExcludeId) //不包含排除ID
                .Where(t=> !input.Name.IsNullOrWhiteSpace() && t.Name.Contains(input.Name)) //如需要精确匹配则用Equals                                                                                             //年龄区间查询
                .Where(t=> input.AgeStart.HasValue && t.Age >= input.AgeStart.Value)
                .Where(t => input.AgeEnd.HasValue && t.Age <= input.AgeEnd.Value)
                //创建日期区间查询
                .Where(t => input.CreateTimeStart.HasValue && t.CreateTime >= input.CreateTimeStart.Value)
                .Where(t => input.CreateTimeEnd.HasValue && t.CreateTime <= input.CreateTimeEnd.Value)
                ;

            return query;
        }

复制代码

这个处理方式类似于常规关系型数据库的处理方式,就是对条件的判断处理。而具体的业务对象模型,和常规框架的实体类很类似。

复制代码

    /// <summary>
    /// 客户信息
    /// 继承自BaseMongoEntity,拥有Id主键属性
    /// </summary>
    public class CustomerInfo : BaseMongoEntity
    {
        /// <summary>
        /// 默认构造函数(需要初始化属性的在此处理)
        /// </summary>
        public CustomerInfo()
        {
            this.CreateTime = System.DateTime.Now;
        }

        #region Property Members

        /// <summary>
        /// 姓名
        /// </summary>
        public virtual string Name { get; set; }
        /// <summary>
        /// 年龄
        /// </summary>
        public virtual int Age { get; set; }
        /// <summary>
        /// 创建人
        /// </summary>
        public virtual string Creator { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public virtual DateTime CreateTime { get; set; }

        #endregion
    }

复制代码

对于插入和更新操作等常规操作,我们调用普通的Collection操作处理就可以了

复制代码

        /// <summary>
        /// 创建对象
        /// </summary>
        /// <param name="input">实体对象</param>
        /// <returns></returns>
        public virtual async Task InsertAsync(TEntity input)
        {
            SetObjectIdIfEmpty(input);//如果Id为空,设置为ObjectId的值
            await collection.InsertOneAsync(input);
        }

        /// <summary>
        /// 更新记录
        /// </summary>
        public virtual async Task<bool> UpdateAsync(TEntity input)
        {
            SetObjectIdIfEmpty(input);//如果Id为空,设置为ObjectId的值

            //await _dbSet.ReplaceOneAsync(Builders<TEntity>.Filter.Eq("_id", input.Id), input);

            //要修改的字段
            var list = new List<UpdateDefinition<TEntity>>();
            foreach (var item in input.GetType().GetProperties())
            {
                if (item.Name.ToLower() == "id") continue;
                list.Add(Builders<TEntity>.Update.Set(item.Name, item.GetValue(input)));
            }
            var updatefilter = Builders<TEntity>.Update.Combine(list);
            var update = await collection.UpdateOneAsync(Builders<TEntity>.Filter.Eq("_id", input.Id), updatefilter);
            var result = update != null && update.ModifiedCount > 0;
            return result;
        }

复制代码

更新操作,有一种整个替换更新,还有一个是部分更新,它们两者是有区别的。如果对于部分字段的更新,那么操作如下所示 ,主要是利用UpdateDefinition对象来指定需要更新那些字段属性及值等信息。

复制代码

        /// <summary>
        /// 封装处理更新的操作(部分字段更新)
        /// </summary>
        /// <example>
        ///  var update = Builders<UserInfo>.Update.Set(s => s.Name, newName);
        /// </example>
        public virtual async Task<bool> UpdateAsync(string id, UpdateDefinition<TEntity> update)
        {
            var result = await collection.UpdateOneAsync(s => s.Id == id, update, new UpdateOptions() { IsUpsert = true });
            return result != null && result.ModifiedCount > 0;
        }

复制代码

根据MongoDB数据库的特性,我们尽量细化对数据库操作的基类接口,定义所需的接口函数即可。

对于Web API的控制器设计,我们在之前的随笔也有介绍,为常规授权处理的BaseApiController,为常规业务CRUD等接口处理的BusinessController,如下所示。

其中ControllerBase是.net core Web API中的标准控制器基类,我们由此派生一个LoginController用于登录授权,而BaseApiController则处理常规接口用户身份信息,而BusinessController则是对标准的增删改查等基础接口进行的封装,我们实际开发的时候,只需要开发编写类似CustomerController基类即可。

而对于 MongoDB的Web API控制器,我们为了方便开发,也设计了同类型的Web API 控制器基类。

其中MongoBaseController基类具有常规的CRUD的接口定义处理,只要继承它就可以了,而如果只是继承BaseApiController这需要自定义控制器接口的方法。

其中MongoBaseController 的基类代码部分如下所示(通过注入Service层的接口实现业务调用)。

复制代码

    /// <summary>
    /// 基于MongoDB的业务操作接口封装的基类控制器
    /// </summary>
    public class MongoBaseController<TEntity, TGetListInput> : BaseApiController
        where TEntity : class, IEntity<string>, new()
        where TGetListInput : IPagedAndSortedResultRequest
    {
        /// <summary>
        /// 通用基础操作接口
        /// </summary>
        protected IBaseMongoService<TEntity, TGetListInput> _service { get; set; }

        /// <summary>
        /// 构造函数,初始化基础接口
        /// </summary>
        /// <param name="service">通用基础操作接口</param>
        public MongoBaseController(IBaseMongoService<TEntity, TGetListInput> service)
        {
            this._service = service;
        }        
        /// <summary>
        /// 获取所有记录
        /// </summary>
        [HttpGet]
        [Route("all")]
        public virtual async Task<ListResultDto<TEntity>> GetAllAsync()
        {
            //检查用户是否有权限,否则抛出MyDenyAccessException异常
            base.CheckAuthorized(AuthorizeKey.ListKey);
            return await _service.GetAllAsync();
        }
        /// <summary>
        /// 根据条件获取列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("list")]
        public virtual async Task<PagedResultDto<TEntity>> GetListAsync([FromQuery] TGetListInput input)
        {
            //检查用户是否有权限,否则抛出MyDenyAccessException异常
            base.CheckAuthorized(AuthorizeKey.ListKey);
            return await _service.GetListAsync(input);
        }
        /// <summary>
        /// 根据ID获取单一对象
        /// </summary>
        /// <param name="id">主键ID</param>
        /// <returns></returns>
        [HttpGet]
        [Route("{id}")]
        public virtual async Task<TEntity> GetAsync(string id)
        {
            //检查用户是否有权限,否则抛出MyDenyAccessException异常
            base.CheckAuthorized(AuthorizeKey.ViewKey);
            return await _service.GetAsync(id);
        }

 .................

复制代码

最后我们启动Swagger进行测试对应的接口即可,实际还可以整合在UI中进行测试处理。我们安装MongoDB数据库的管理工具后,可以在MongoDBCompass 中进行查询对应数据库的数据。

复制代码

    /// <summary>
    /// 客户信息的控制器对象(基于MongoDB),基于BaseApiController,需要自定义接口处理
    /// </summary>
    [ApiController]
    [Route("api/MongoCustomer")]
    public class MongoCustomerController : BaseApiController
    {
        private ICustomerService _service;

        /// <summary>
        /// 构造函数,并注入基础接口对象
        /// </summary>
        /// <param name="service"></param>
        public MongoCustomerController(ICustomerService service) 
        {
            this._service = service;
        }

        /// <summary>
        /// 获取所有记录
        /// </summary>
        [HttpGet]
        [Route("all")]
        public virtual async Task<ListResultDto<CustomerInfo>> GetAllAsync()
        {
            //检查用户是否有权限,否则抛出MyDenyAccessException异常
            base.CheckAuthorized(AuthorizeKey.ListKey);

            return await _service.GetAllAsync();
        }

复制代码

 

 而如果继承自MongoBaseController ,那么就会具有基类MongoBaseController 公开的所有控制器方法。 

复制代码

    /// <summary>
    /// 客户信息的控制器对象(基于MongoDB),基于MongoBaseController,具有常规CRUD操作接口
    /// </summary>
    [ApiController]
    [Route("api/MongoCustomer2")]
    public class MongoCustomer2Controller : MongoBaseController<CustomerInfo, CustomerPagedDto>
    {
        /// <summary>
        /// 构造函数,并注入基础接口对象
        /// </summary>
        /// <param name="service"></param>
        public MongoCustomer2Controller(ICustomerService service) : base(service)
        {
        }
    }

复制代码

 

 

早几年前曾经也介绍过该数据库的相关使用,随笔如下所示,有需要也可以了解下。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/62790.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SPI通信协议

简介 SPI有主、从两种模式&#xff0c;通常由一个主模块和一个或多个从模块组成&#xff08;SPI不支持多主机&#xff09;&#xff0c;主 模块选择一个从模块进行同步通信&#xff0c;从而完成数据的交换。提供时钟的为主设备&#xff08;Master&#xff09;&#xff0c;接 收时…

AlamLinux 9 显示最小化按钮

AlamLinux 9 显示最小化按钮 默认情况下&#xff0c;AlamLinux 9 是没有最小化按钮的&#xff0c;在操作上会影响我们的工作效率。 想加上最小化按钮也非常简单&#xff0c;1行命令就能解决。 gsettings set org.gnome.desktop.wm.preferences button-layout ":minimize…

<dependency> idea中为什么这个变黄色

在IDE中&#xff0c;当你的代码出现黄色高亮时&#xff0c;通常表示存在警告或建议的提示。对于Maven的<dependency>标签来说&#xff0c;黄色高亮可能有以下几种原因&#xff1a; 依赖项未找到&#xff1a;黄色高亮可能表示IDE无法找到指定的依赖项。这可能是由于配置错…

Android中级——RemoteView

RemoteView RemoteView的应用NotificationWidgetPendingIntent RemoteViews内部机制模拟RemoteViews RemoteView的应用 Notification 如下开启一个系统的通知栏&#xff0c;点击后跳转到某网页 public class MainActivity extends AppCompatActivity {private static final …

面试热题(翻转k个链表)

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值&a…

无涯教程-Lua - Modules(模块)

模块就像可以使用 require 加载的库&#xff0c;并且具有包含Table的单个全局名称&#xff0c;该模块可以包含许多函数和变量。 Lua 模块 其中一些模块示例如下。 -- Assuming we have a module printFormatter -- Also printFormatter has a funtion simpleFormat(arg) -- …

Godot 4 练习 - 制作粒子

演示项目dodge_the_creeps中&#xff0c;有一个Trail&#xff0c;具体运行效果 想要看看咋实现的&#xff0c;看完也不清晰&#xff0c;感觉是要设置某些关键的属性 ChatGPT说&#xff1a;以下是一些重要的属性&#xff1a; texture&#xff1a;用于渲染粒子的纹理。您可以使用…

迅为全国产龙芯3A5000电脑运行统信UOS、银河麒麟、loongnix系统

iTOP-3A5000开发板采用全国产龙芯3A5000处理器&#xff0c;基于龙芯自主指令系统 (LoongArch) 的LA464微结构&#xff0c;并进一步提升频率&#xff0c;降低功耗&#xff0c;优化性能。在与龙芯3A4000处理器保持引脚兼容的基础上&#xff0c;频率提升至2.5GHZ&#xff0c;功耗降…

使用Socket实现UDP版的回显服务器

文章目录 1. Socket简介2. DatagramSocket3. DatagramPacket4. InetSocketAddress5. 实现UDP版的回显服务器 1. Socket简介 Socket&#xff08;Java套接字&#xff09;是Java编程语言提供的一组类和接口&#xff0c;用于实现网络通信。它基于Socket编程接口&#xff0c;提供了…

《HeadFirst设计模式(第二版)》第七章代码——适配器模式

代码文件目录&#xff1a; Example1: Duck package Chapter7_AdapterAndFacadePattern.Adapter.Example1;/*** Author 竹心* Date 2023/8/7**/public interface Duck {public void quack();public void fly(); }DuckTestDrive package Chapter7_AdapterAndFacadePattern.Ada…

IO学习-有名管道

1&#xff0c;要求实现AB进程对话 A进程先发送一句话给B进程&#xff0c;B进程接收后打印 B进程再回复一句话给A进程&#xff0c;A进程接收后打印 重复1.2步骤&#xff0c;当收到quit后&#xff0c;要结束AB进程 运行结果&#xff1a;

TS协议之PES(ES数据包)

TS协议之PAT&#xff08;节目关联表&#xff09;TS协议之PMT&#xff08;节目映射表&#xff09;TS协议之PES&#xff08;ES数据包&#xff09; 1. 概要 1.1 TS数据包&#xff08;PES&#xff09;协议数据组成 TSTS头PES头ES。TS&#xff0c;PES头是在音视频流传输过程中需要…

彩虹云商城搭建完整教程 完整的学习资料

彩虹云商城搭建完整教程 完整的学习资料提供给大家学习 随着电子商务的快速发展&#xff0c;越来越多的企业开始意识到开设一个自己的电子商城对于销售和品牌推广的重要性。然而&#xff0c;选择一家合适的网站搭建平台和正确地构建一个商城网站并不是一件容易的事情。本文将为…

Docker安装Mysql、Redis、nginx、nacos等环境

相关系列文章&#xff1a; 1、DockerHarbor私有仓库快速搭建 2、DockerJenkinsHarbor 1、服务器 Ip部署内容说明192.168.88.7Docker、Mysql、redis、nacosnode1192.168.88.8Docker、Mysql、redis、nacosnode2192.168.88.9Docker、redis、nacos、nginxnode3 2、安装PXC8.0 Mys…

路由的hash和history模式的区别

目录 ✅ 路由模式概述 一. 路由的hash和history模式的区别 1. hash模式 2. history模式 3. 两种模式对比 二. 如何获取页面的hash变化 ✅ 路由模式概述 单页应用是在移动互联时代诞生的&#xff0c;它的目标是不刷新整体页面&#xff0c;通过地址栏中的变化来决定内容区…

WSL 2 installation is incomplete的解决方案

问题描述 解决方案 在Windows功能中开启Hyper-v 如果没有Hyper-v选项&#xff0c;新建文本粘贴以下内容后以.cmd为后缀保存后执行即可 pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt for /f %%i in (findstr /i . hyper-v.t…

matlab智能算法程序包89套最新高清录制!matlab专题系列!

关于我为什么要做代码分享这件事&#xff1f; 助力科研旅程&#xff01; 面对茫茫多的文献&#xff0c;想复现却不知从何做起&#xff0c;我们通过打包成品代码&#xff0c;将过程完善&#xff0c;让您可以拿到一手的复现过程以及资料&#xff0c;从而在此基础上&#xff0c;照…

运动耳机哪个最好、顶级运动耳机推荐

拥有一款出色的运动耳机&#xff0c;是每个运动爱好者追求完美体验的必备选择。今天&#xff0c;我为大家推荐五款顶级运动耳机&#xff0c;它们不仅将音乐和运动完美结合&#xff0c;还具备出色的防水性能、舒适的佩戴感和激动人心的音质表现&#xff0c;让你在运动中尽情释放…

【2.2】Java微服务:nacos的使用

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a; 深度学习 ✨特色专栏&#xff1a; 知识分享 &…

企业工程项目管理系统源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)em

​ 工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…