【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这个开源仓储实现类。如果本文对你有帮助的话,请点赞+评论+关注,或者转发给需要的朋友。

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

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

相关文章

Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs

大开眼界&#xff1f;探索多模态模型种视觉编码器的缺陷。 论文中指出&#xff0c;上面这些VQA问题&#xff0c;人类可以瞬间给出正确的答案&#xff0c;但是多模态给出的结果却是错误的。是哪个环节出了问题呢&#xff1f;视觉编码器的问题&#xff1f;大语言模型出现了幻觉&…

章鱼网络 Community Call #17|打造全新 Omnity 跨链协议

香港时间2024年1月8日12点&#xff0c;章鱼网络举行第17期 Community Call。 对于 Octopus Community 而言&#xff0c;2023年是一个分水岭。我们如期兑现我们的承诺&#xff0c;成功上线了包括 $NEAR Restaking 和 Adaptive IBC 在内的完整的 Octopus 2.0。 自从我们在2023年…

HarmonyOS 应用开发入门

HarmonyOS 应用开发入门 前言 DevEco Studio Release版本为&#xff1a;DevEco Studio 3.1.1。 Compile SDK Release版本为&#xff1a;3.1.0&#xff08;API 9&#xff09;。 构建方式为 HVigor&#xff0c;而非 Gradle。 最新版本已不再支持 &#xff08;”Java、JavaScrip…

Leetcode的AC指南 —— 栈与队列:225.用队列实现栈

摘要&#xff1a; **Leetcode的AC指南 —— 栈与队列&#xff1a;225.用队列实现栈 **。题目介绍&#xff1a;请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 …

web渗透安全学习笔记:2、HTML基础知识

目录 前言 HTML的标题 段落链接与插入图片 HTML元素 HTML属性 HTML头部 HTML与CSS HTML与JavaScript 表格与列表 HTML区块 布局 HTML表单 HTML与数据库 音频与视频 HTML事件 前言 HTML的标题 <!DOCTYPE html> <html> <head> <meta chars…

Spring+SprinMVC+MyBatis注解方式简易模板

SpringSprinMVCMyBatis注解方式简易模板代码Demo GitHub访问 ssm-tpl-anno 一、数据准备 创建数据库test&#xff0c;执行下方SQL创建表ssm-tpl-cfg /*Navicat Premium Data TransferSource Server : 127.0.0.1Source Server Type : MySQLSource Server Version :…

HCIP-BGP实验3

实验步骤 配置IP地址 R1 [r1]int g0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int loopback0 [r1-LoopBack0]ip add 192.168.1.1 24 [r1-LoopBack0]int loopback1 [r1-LoopBack1]ip add 192.168.2.1 24 [r1-LoopBack1]int loopback3 [r1-…

OCS2 入门教程(六)- Double Integrator

系列文章目录 前言 双积分器示例是我们最简单的问题。它模拟了一个沿 x 方向移动的一维点质量。模型是线性的&#xff0c;成本函数是二次函数。目标点通过参考管理器模块设置为二次成本。 一、查看文件结构 1.1 ocs2_double_integrator 文件夹 . ├── auto_generated ├─…

LSTM时间序列预测

本文借鉴了数学建模清风老师的课件与思路&#xff0c;可以点击查看链接查看清风老师视频讲解&#xff1a;【1】演示&#xff1a;基于LSTM深度学习网络预测时间序列&#xff08;MATLAB工具箱&#xff09;_哔哩哔哩_bilibili % Forecast of time series based on LSTM deep learn…

Pyro —— Velocity Voxel Scale

Velocity Voxel Scale是H19.5引入的新参数&#xff0c;该参数可单独定义volume和速度体素&#xff1b;根据参数设置&#xff0c;可观察到模拟时间的显著变化&#xff1b; Velocity Voxel Scale对DOP和SOP均可用&#xff1b;对DOP设置&#xff0c;该参数在Smoke Object&#xf…

Java安全 CC链1分析

Java安全之CC链1分析 什么是CC链环境搭建jdk下载idea配置创建项目 前置知识Transformer接口ConstantTransformer类invokerTransformer类ChainedTransformer类 构造CC链1CC链1核心demo1demo1分析 寻找如何触发CC链1核心TransformedMap类AbstractInputCheckedMapDecorator类readO…

RT-Thread 瑞萨 智能家居网络开发:RA6M3 HMI Board 以太网+GUI技术实践

不用放大了&#xff0c; 我在包里找到张不小的…… 以太网HMI线下培训-环境准备 这是社群的文档&#xff1a;【腾讯文档】以太网线下培训&#xff08;HMI-Board&#xff09; https://docs.qq.com/doc/DY0FIWFVuTEpORlNn 先介绍周六的培训是啥&#xff0c;然后再介绍一下要准…

赛车游戏简单单车C语言版

#include<stdio.h> #include<easyx.h> #include<time.h>#define WIDTH 512 #define HEIGHT 768//定义一个汽车类 struct FCar {//坐标float x, y;// 汽车种类int type;//汽车速度float speed; };//定义全局变量 图片坐标 IMAGE BG_IMG; //背景图片坐标 float…

[小程序]使用代码渲染页面

一、条件渲染 1.单个控制 使用wx:if"{{条件}}"来判断是否需要渲染这段代码&#xff0c;同时可以结合wx:elif和wx:else来判断 <view wx:if"{{type0}}">0</view> <view wx:elif"{{type1}}">1</view> <view wx:else>…

linux网络协议栈2--网络包接收发送流程

上文我们讲了报文格式&#xff0c;应该对数据传输格式有了一定了解&#xff0c;这篇文章主要讲述的是网络包接收和发送的流程&#xff0c;主要是大方面来介绍。 网络包接收流程 当网络数据帧通过网络传输到达网卡时&#xff0c;网卡会将网络数据帧通过DMA的方式放到环形缓冲区…

nginx日志分割

日志切割是线上常见的操作&#xff0c;能够控制单个日志文件的大小&#xff0c;便于对日志进行管理 给nginx主进程发送一个重新打开的信号&#xff0c;让nginx重新生成新的日志文件 nginx -s reopen 这个命令等同于kill -USR1 cat nginx.pid 切割日志文件shell命令 #!/bin/bas…

Flink处理函数(3)—— 窗口处理函数

窗口处理函数包括&#xff1a;ProcessWindowFunction 和 ProcessAllWindowFunction 基础用法 stream.keyBy( t -> t.f0 ).window( TumblingEventTimeWindows.of(Time.seconds(10)) ).process(new MyProcessWindowFunction()) 这里的MyProcessWindowFunction就是ProcessWi…

rk1126, 实现 yolov8 目标检测

基于 RKNN 1126 实现 yolov8 目标检测 Ⓜ️ RKNN 模型转换 ONNX yolo export model./weights/yolov8s.pt formatonnx导出 RKNN 这里选择输出 concat 输入两个节点 onnx::Concat_425 和 onnx::Concat_426 from rknn.api import RKNNONNX_MODEL ./weights/yolov8s.onnxRKNN_MOD…

C语言练习day8

变种水仙花 变种水仙花_牛客题霸_牛客网 题目&#xff1a; 思路&#xff1a;我们拿到题目的第一步可以先看一看题目给的例子&#xff0c;1461这个数被从中间拆成了两部分&#xff1a;1和461&#xff0c;14和61&#xff0c;146和1&#xff0c;不知道看到这大家有没有觉得很熟…

Spring Boot整合Redis的高效数据缓存实践

引言 在现代Web应用开发中&#xff0c;数据缓存是提高系统性能和响应速度的关键。Redis作为一种高性能的缓存和数据存储解决方案&#xff0c;被广泛应用于各种场景。本文将研究如何使用Spring Boot整合Redis&#xff0c;通过这个强大的缓存工具提高应用的性能和可伸缩性。 整合…