从CRUD到高级功能:EF Core在.NET Core中全面应用(三)

news2025/1/21 0:40:33

目录

IQueryable使用

原生SQL使用

实体状态跟踪

全局查询筛选器

并发控制使用


IQueryable使用

        在EFCore中IQueryable是一个接口用于表示可查询的集合,它继承自IEnumerable但具有一些关键的区别,使得它在处理数据库查询时非常有用,普通集合的版本(IEnumerable)是在内存中过滤(客户端评估),而IQueryable版本则是把查询操作翻译成SQL语句(服务器端评估):

接下来我们开始讲解其简单的应用,如下所示是两种使用的代码,基本上都一样,唯一区别在于两种在执行查询时的行为有所不同,如下所示:

IQueryable:查询会被延迟执行并且它能将LINQ查询转换为SQL以便在数据库中执行,适合处理大规模数据。
IEnumerable:查询会立即执行适合用于内存中的数据集合但无法进行数据库优化,因此性能较差。

class Program
{
    static void Main(string[] args)
    {
        using (MyDbContext ctx = new MyDbContext())
        {
            IEnumerable<Class> classes1 = ctx.Classes.Include(t => t.Students);
            IQueryable<Class> classes2 = ctx.Classes.Include(t => t.Students);
            foreach (var c in classes1)
            {
                Console.WriteLine($"Class Name: {c.Name}");
                foreach (var s in c.Students)
                {
                    Console.WriteLine($"\tStudent Name: {s.Name}, Age: {s.Age}");
                }
            }
            foreach (var c in classes2)
            {
                Console.WriteLine($"Class Name: {c.Name}");
                foreach (var s in c.Students)
                {
                    Console.WriteLine($"\tStudent Name: {s.Name}, Age: {s.Age}");
                }
            }
        }
    }
}

两者的区别如下所示:

特性IQueryableIEnumerable
执行时机延迟执行,查询会转化为 SQL 并在数据库中执行即时执行,查询在内存中执行
数据来源通常用于数据库查询,支持远程数据源通常用于内存中的集合
性能优化利用数据库的索引和优化进行查询数据已经在内存中,无法优化
常见用途用于数据库查询(如 Entity Framework)用于内存中的集合,如 List、Array 等

延迟执行:IQueryable支持延迟加载,执行查询的集合时只有在查询被执行时才会真正访问数据库或数据源,查询会在执行时转化为相应的SQL语句或者其他数据源的查询语言,只有在需要数据时查询才会被执行,如下所示:

// 假设 context 是一个 DbContext 对象
IQueryable<User> query = context.Users.Where(u => u.Age > 18);
// 这个查询还没有执行,只有调用 ToList() 或其他方法时,查询才会发送到数据库执行
var result = query.ToList(); // 执行查询,返回结果

IQueryable只是代表一个“可以放到数据库服务器去执行的查询”,它没有立刻执行只是可以被执行而已,对于IQueryable接口调用非终结方法的时候不会执行查询,而调用终结方法的时候则会立刻执行查询。

一个方法的返回值类型如果是IQueryable类型,那么这个方法一般就是非终结方法,否则就是终结方法:

// 终结方法:
ToArrar()、ToList()、Min()、Max()、Count()等

// 非终结方法:
GroupBy()、OrderBy()、Include()、Skip()、Take()等

IQueryable代表一个对数据库中数据进行查询的一个逻辑,这个查询是一个延迟查询,我们可以调用非终结方法向IQueryable中添加查询逻辑,当执行终结方法的时候才真正生成SQL语句来执行查询,可以实现以前要靠SQL拼接实现的动态查询逻辑。

分页查询:IQueryable是用于支持延迟执行的LINQ查询的一种接口,在分页查询的实现中我们通常结合Skip()和Take()方法来控制结果集的起始位置和返回的数量,分页查询通过限制数据的加载量来提高查询效率,尤其在处理大数据集时,如下所示:

// Skip() 方法: 用于跳过前N条记录,通常用来跳过前几页的数据。
// Take() 方法: 用于返回指定数量的记录。
public IQueryable<T> GetPagedResults<T>(IQueryable<T> query, int pageNumber, int pageSize)
{
    return query.Skip((pageNumber - 1) * pageSize)  // 跳过前几页的记录
                .Take(pageSize);                  // 获取当前页的记录
}

这里我们通过分页去查询我们数据库当中的学生表,以每页2条数据为准,如下所示:

获取数据: 从数据库获取数据主要分为以下两种:

1)DataReader:分批从数据库服务器读取数据,内存占用小、DB连接占用时间长。

2)DataTable:把所有数据都一次性从数据库服务器都加载到客户端内存中,内存占用大节省DB连接。

我们可以通过insert into select多插入一些数据,然后加上Delay/Sleep的遍历IQueryable,在遍历执行的过程中停止MySQL服务器,可以验证IQueryable内部就是在调用DataReader,其优点是节省客户端内存,缺点是如果处理的慢就会长时间占用连接。

如果想IQueryable一次性加载数据到内存中,可以用IQueryable的ToArrary()、ToArrayAsync()、ToList()、ToListAsync()等方法,等ToArray()执行完毕再断服务器试一下。

原生SQL使用

尽管EF Core已经非常强大,但是仍然存在着无法被写成标准EF Core调用方法的SQL语句,少数情况下仍然需要写原生的SQL,这里有三种情况:非查询语句、实体查询、任意SQL查询,如下:

非查询语句:这里我们可以通过ExecuteSqlInterpolatedAsync进行字符串插值拼接,其会自动处理SQL参数,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string description = "test";
            using (MyDbContext ctx = new MyDbContext())
            {
                await ctx.Database.ExecuteSqlInterpolatedAsync(
                    $"INSERT INTO T_Class (Name, Description) SELECT Name, {description} FROM T_Students WHERE Id > 2"
                );
            }
        }
    }
}

得到的结果如下所示:

字符串内插如果赋值给string变量,就是字符串拼接,字符串内插如果赋值FormattableString变量,编译器就会构造FormattableString对象,该对象会进行参数化SQL处理,一定程度上防止了SQL注入攻击,如下所示:

实体查询:如果要执行的原生SQL是一个查询语句并且查询的结果也能对应一个实体,就可以调用对应实体的DbSet的FromSqlInterpolated()方法来执行一个查询的SQL语句,同样使用字符串内插来传递参数,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string description = "%test%";
            using (MyDbContext ctx = new MyDbContext())
            {
                var queryable = ctx.Classes.FromSqlInterpolated(
                    @$"select * from T_Class where Description like {description}"
                );
                foreach (var item in queryable)
                {
                    Console.WriteLine(item.Id+" "+item.Name+" "+item.Description);
                }
            }
        }
    }
}

查询的结果如下所示:

        FromSqlInterpolated()方法的返回值是IQueryable类型的,因此我们可以在执行IQueryable之前对IQueryable进行进一步的处理,把只能用原生SQL语句写的逻辑用FromSqlInterpolated()去执行,然后把分页、分组、二次过滤、排序、Include等其他逻辑尽可能仍然使用EF Core的标准去操作实现。

局限性:SQL查询必须返回实体类型对应数据库表的所有列,结果集中的列必须与属性映射到的列名称匹配,只能单表查询而不能使用join语句进行关联查询,但是可在查询后面使用Include来进行关联数据的获取。

任意SQL查询:这里使用DbConnection来获取数据库的连接对象,而不借用EF Core生成的SQL进行查询,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                DbConnection conn = ctx.Database.GetDbConnection(); // 获取数据库连接
                if (conn.State != System.Data.ConnectionState.Open)
                {
                    await conn.OpenAsync(); // 打开数据库连接
                }
                using (DbCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "select * from T_Class where Description like '%test%'";
                    using (DbDataReader reader = await cmd.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            Console.WriteLine(reader["Id"] + " " + reader["Name"] + " " + reader["Description"]);
                        }
                    }
                }

            }
        }
    }
}

最终得到的结果如下所示:

总结:一般Ling操作就够了尽量不用写原生SQL,非查询SQL用ExecuteSqllnterpolated(),针对实体的SQL查询用FromSqllnterpolated(),复杂SQL查询用ado.net的方式或者Dapper等

实体状态跟踪

实体状态跟踪:是指框架如何追踪实体对象在其生命周期中的状态变化,这些状态帮助EFCore确定如何与数据库进行交互,以便在保存更改时正确生成SQL查询。

EFCore使用一个叫做Change Tracker的机制来跟踪实体的状态,确保数据库中的数据与应用程序中的实体对象保持一致,其实体对象主要有以下五种状态:

已添加(Added):实体是新创建的尚未保存到数据库中,EFCore将会把这些实体作为新的记录插入到数据库中,如下:

var newStudent = new Student { Name = "John" };
dbContext.Students.Add(newStudent);  // 设置状态为 Added

未改变(Unchanged):实体的状态没有发生变化,EFCore不会对其生成任何SQL语句,实体的属性值与数据库中的数据一致,如下:

var student = dbContext.Students.Find(1);  // 假设没有更改属性
// 没有显式的修改,实体状态保持 Unchanged

已修改(Modified):实体已存在并且它的属性值发生了变化,EFCore会在保存更改时生成一个 update sql语句将这些更改同步到数据库中,如下:

var student = dbContext.Students.Find(1);
student.Name = "Jane";  // 设置状态为 Modified
dbContext.Students.Update(student);

已删除(Deleted):实体被标记为删除并且当保存更改时,EFCore会生成deleted sql语句,需要显式调用删除操作来设置实体为删除状态,如下:

var student = dbContext.Students.Find(1);
dbContext.Students.Remove(student);  // 设置状态为 Deleted

已分离(Detached):实体不再被EFCore上下文跟踪,通常是因为实体从上下文中移除或在数据库之外创建,如果试图对这些实体做更改EFCore不会追踪它们,因此不会执行任何操作,如下:

var student = dbContext.Students.Find(1);
dbContext.Entry(student).State = EntityState.Detached;  // 设置状态为 Detached

如果想查看实体状态,这里我们可以使用DbContext的Entry()方法来获得实体在EF Core中的跟踪信息对象EntityEntry,EntityEntry类的State属性代表实体的状态,通过DebugView.LongView属性可以看到实体的变化信息,如下所示:

var student = dbContext.Students.Find(1);
var entry = dbContext.Entry(student);
Console.WriteLine(entry.State);  // 输出实体的当前状态(例如 Added, Modified, Unchanged, 等)

DbContext会根据跟踪的实体的状态,在SaveChanges()的时候,根据实体状态的不同生成update、delete、insert等sql语句来把内存中实体的变化更新到数据库中。

默认情况下EFCore会追踪所有实体的状态,如果不想追踪某些实体的状态可以使用AsNoTracking方法禁用状态跟踪,这通常用于查询操作以提高性能:

var students = dbContext.Students.AsNoTracking().ToList();

如果我们确认我们的操作只会查询不会被修改、删除等,那么这里我们就可以使用AsNoTracking()方法来提升性能,降低内存占用,如下所示:

全局查询筛选器

EFCore中的全局查询筛选器是一种用于在整个应用程序中自动应用的查询条件,它允许在查询时自动对数据进行过滤,确保数据的一致性和安全性而无需在每个查询中显式添加筛选条件,常用的场景如下所示:

软删除:通过全局查询筛选器确保删除的记录在查询中不被返回,而无需显式地为每个查询添加where子句。

假设我们有一个Product实体,其中包含一个IsDeleted属性用来标记某个产品是否被删除,我们可以在OnModelCreating方法中为Product实体配置全局查询筛选器确保查询时自动排除已删除的产品,如下所示:

public class ApplicationDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 为 Product 实体添加全局查询筛选器
        modelBuilder.Entity<Product>()
            .HasQueryFilter(p => !p.IsDeleted);
    }
}

如果想查询已经删除的数据,并且我们也已经配置了全局忽略删掉数据的过滤器,这里我们可以在要查询删除数据的地方添加上忽略全局过滤器的函数,如下所示:

多租户:为每个租户添加自动的筛选条件,确保每个租户只访问自己的数据。

这里可以使用多个条件创建复杂的筛选器,例如如果有一个TenantId字段来支持多租户功能,可以根据租户ID创建全局筛选器,这种方式确保了在每次查询Product实体时都会自动根据当前租户的ID过滤数据,如下所示:

modelBuilder.Entity<Product>()
    .HasQueryFilter(p => p.TenantId == currentTenantId);

并发控制使用

并发控制用于确保多个用户或多个进程对数据库进行并发访问时,不会产生数据冲突或不一致的问题,避免多个用户同时操作资源造成的并发冲突问题,例如:统计点击量。最好的解决方案就是:非数据库解决方案。如果从数据库层面来处理的话,EFCore支持如下两种主要的并发控制机制:

悲观并发控制:假设并发冲突的可能性较大,因此会通过锁定数据来防止其他用户修改正在处理的数据。在悲观并发控制中EFCore支持使用数据库的锁机制(如行级锁)来实现并发控制。

悲观并发控制一般采用行锁、表锁等排他锁对资源进行锁定,确保同时只有一个使用者操作被锁定的资源,EF Core没有封装悲观并发控制的使用,需要开发人员编写原生SQL语句来使用悲观并发控制,不同数据库的语法不一样。

这里我们通过一个占据房子书写房名的案例进行讲解,这里通过MySQL方案来实现,如下:

class House {
    public long Id {get; set;}
    public string Name {get; set;}
    public string Owner {get; set;}
}
// MySQL方案
select * from T_Houses Where Id = 1 for update

如果有其他的查询操作也使用for update来查询id=1的这条数据的话,那么查询就会被挂起直到针对这条数据的更新操作完成从而释放这个行锁,代码才会继续执行,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("请输出您的名字");
            string name = Console.ReadLine();
            using (MyDbContext ctx = new MyDbContext())
            {
                var houses = ctx.Houses.Single(h => h.Id == 1);
                if (!string.IsNullOrEmpty(houses.Owner))
                {
                    if (houses.Owner == name)
                    {
                        Console.WriteLine("恭喜你,你已经买过了");
                    } else
                    {
                        Console.WriteLine($"房子已经被{houses.Owner}买走了");
                    }
                    return;
                }
                houses.Owner = name;
                Thread.Sleep(10000);
                Console.WriteLine("恭喜你,你已经买到了房子");
                ctx.SaveChanges();
                Console.ReadKey();
            }
        }
    }
}

上面代码,如果我们不进行并发控制的话,下面如果我们同时执行两个人抢房子,两者都会出现买到了房子,但是实际上房子最后还是被Hack买到了,如下

接下来我们在程序中添加事务操作,如下所示:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("请输出您的名字");
            string name = Console.ReadLine();
            using (MyDbContext ctx = new MyDbContext())
            using (var tx = ctx.Database.BeginTransaction()) // 开启事务
            {
                Console.WriteLine(DateTime.Now+"正在为您查询房源信息");
                var houses = ctx.Houses.FromSqlInterpolated($"select * from T_House where Id = 1 for update").Single();
                Console.WriteLine(DateTime.Now+"房源信息完毕");
                if (!string.IsNullOrEmpty(houses.Owner))
                {
                    if (houses.Owner == name)
                    {
                        Console.WriteLine("恭喜你,你已经买过了");
                    } else
                    {
                        Console.WriteLine($"房子已经被{houses.Owner}买走了");
                    }
                    Console.ReadKey();
                    return;
                }
                houses.Owner = name;
                Thread.Sleep(10000);
                Console.WriteLine("恭喜你,你已经买到了房子");
                ctx.SaveChanges();
                Console.WriteLine("正在为您保存房源信息");
                tx.Commit(); // 提交事务
                Console.ReadKey();
            }
        }
    }
}

得到的结果如下所示,可以看到我们的并发操作以及处理好了:

总结: 悲观并发控制的使用比较简单,锁是独占排他的,如果系统并发量很大的话会严重影响性能,如果使用不当的话甚至会导致死锁,这点尤为重要,所以要根据实际情况进行选择使用。

乐观并发控制:假设并发冲突较少,因此允许多个操作同时对数据进行修改,在提交更改时EFCore会检查数据是否被其他操作修改过,如果数据已被修改当前操作会被拒绝并抛出 DbUpdateConcurrencyException异常。

举例:当update的时候如果数据库中的Owner值已经被其他操作者更新为其他值了,那么where语句的值就会被设为false,因此这个update语句影响的行数就是0,EFCore就知道发生并发冲突了,因此SaveChanges()方法就会抛出DbUpdateConcurrencyException异常,如下所示:

1)标记并发字段:在实体类或配置类中标记一个字段作为并发标记,通常是一个时间戳字段或者是一个列,EFCore使用该字段来检测数据是否在并发操作期间被修改过,如下所示:

namespace test
{
    internal class HouseConfig : IEntityTypeConfiguration<House>
    {
        public void Configure(EntityTypeBuilder<House> builder)
        {
            builder.ToTable("T_House");
            builder.Property(x => x.Name).IsRequired();
            builder.Property(x => x.Owner).IsConcurrencyToken(); // 并发标记
        }
    }
}

2)处理并发冲突:当发生并发冲突时,EFCore会抛出DbUpdateConcurrencyException异常,可以捕获此异常并根据需求进行处理,比如:

namespace Program
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("请输出您的名字");
            string name = Console.ReadLine();
            using (MyDbContext ctx = new MyDbContext())
            {
                Console.WriteLine(DateTime.Now + "正在为您查询房源信息");
                var houses = ctx.Houses.Single(h => h.Id == 1);
                Console.WriteLine(DateTime.Now + "房源信息完毕");
                if (!string.IsNullOrEmpty(houses.Owner))
                {
                    if (houses.Owner == name)
                    {
                        Console.WriteLine("恭喜你,你已经买过了");
                    }
                    else
                    {
                        Console.WriteLine($"房子已经被{houses.Owner}买走了");
                    }
                    Console.ReadKey();
                    return;
                }
                houses.Owner = name;
                Console.WriteLine("恭喜你,你已经买到了房子");
                try
                {
                    ctx.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    Console.WriteLine("并发访问冲突");
                    var entry = ex.Entries.Single();
                    string newValue = entry.GetDatabaseValues().GetValue<string>("Owner");
                    Console.WriteLine($"房子已经被{newValue}买走了");
                }
                Console.ReadKey();
            }
        }
    }
}

最终呈现的效果如下所示:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【VRChat · 改模】Unity2019、2022的版本选择哪个如何决策,功能有何区别;

总览 1.Unity2019、2022的版本的选择 2.Unity添加着色器教程 一、Unity2019、2022的版本的选择 1.Unity2019 和 Unity2022 的区别&#xff0c;VRChat SDK 为何要区分两个版本 我是外行&#xff0c;最开始以为的是&#xff0c;2019 和 2022 的变化是基于这个模型本身的。 也…

人工智能之深度学习-[1]-了解深度学习

文章目录 深度学习1. 神经网络的基础2. 深度学习中的重要概念3. 深度学习的工作流程4. 深度学习的应用5. 深度学习的挑战6. 深度学习与传统机器学习的比较7.深度学习的特点 8. 常见的深度学习模型9.深度学习发展史[了解] 深度学习 深度学习&#xff08;Deep Learning&#xff…

彻底理解JVM类加载机制

文章目录 一、类加载器和双亲委派机制1.1、类加载器1.2、双亲委派机制1.3、自定义类加载器1.4、打破双亲委派机制 二、类的加载 图片来源&#xff1a;图灵学院   由上图可知&#xff0c;创建对象&#xff0c;执行其中的方法&#xff0c;在java层面&#xff0c;最重要的有获取…

第148场双周赛:循环数组中相邻元素的最大差值、将数组变相同的最小代价、最长特殊路径、所有安放棋子方案的曼哈顿距离

Q1、循环数组中相邻元素的最大差值 1、题目描述 给你一个 循环 数组 nums &#xff0c;请你找出相邻元素之间的 最大 绝对差值。 **注意&#xff1a;**一个循环数组中&#xff0c;第一个元素和最后一个元素是相邻的。 2、解题思路 这个问题的核心是遍历循环数组并计算相邻…

电脑换固态硬盘

参考&#xff1a; https://baijiahao.baidu.com/s?id1724377623311611247 一、根据尺寸和缺口可以分为以下几种&#xff1a; 1、M.2 NVME协议的固态 大部分笔记本是22x42MM和22x80MM nvme固态。 在京东直接搜&#xff1a; M.2 2242 M.2 2280 2、msata接口固态 3、NGFF M.…

opengrok_windows_环境搭建

软件列表 软件名下载地址用途JDKhttps://download.java.net/openjdk/jdk16/ri/openjdk-1636_windows-x64_bin.zipindex 使用java工具tomcathttps://dlcdn.apache.org/tomcat/tomcat-9/v9.0.98/bin/apache-tomcat-9.0.98-windows-x64.zipweb服务器opengrokhttps://github.com/o…

《offer 来了:Java 面试核心知识点精讲 -- 原理篇》

在 Java 面试的战场上,只知皮毛可不行,面试官们越来越看重对原理的理解。今天就给大家分享一本能让你在面试中脱颖而出的 “武林秘籍”——《offer 来了:Java 面试核心知识点精讲 -- 原理篇》。 本书详细介绍了Java架构师在BAT和移动互联网公司面试中常被问及的核心知识,内…

Linux之网络套接字

Linux之网络套接字 一.IP地址和端口号二.TCP和UDP协议2.1网络字节序 三.socket编程的常见API四.模拟实现UDP服务器和客户端五.模拟实现TCP服务器和客户端 一.IP地址和端口号 在了解了网络相关的基础知识之后我们知道了数据在计算机中传输的流程并且发现IP地址在其中占据了确定…

迈向 “全能管家” 之路:机器人距离终极蜕变还需几步?

【图片来源于网络&#xff0c;侵删】 这是2024年初Figure公司展示的人形机器人Figure 01&#xff0c;他可以通过观看人类的示范视频&#xff0c;在10小时内经过训练学会煮咖啡&#xff0c;并且这个过程是完全自主没有人为干涉的&#xff01; 【图片来源于网络&#xff0c;侵删】…

几何数据结构之四叉树与八叉树

几何数据结构之四叉树与八叉树 四叉树的定义四叉树深度的计算公式推导假设&#xff1a;计算过程&#xff1a;1. 划分空间&#xff1a;2. 节点容纳的最小距离&#xff1a;3. 解出深度&#xff1a;4. 考虑常数项&#xff1a; 总结&#xff1a; 八叉树 四叉树的定义 四叉树&#…

(一)相机标定——四大坐标系的介绍、对应转换、畸变原理以及OpenCV完整代码实战(C++版)

一、四大坐标系介绍 1&#xff0c;世界坐标系 从这个世界&#xff08;world&#xff09;的视角来看物体 世界坐标系是3D空间坐标&#xff0c;每个点的位置用 ( X w , Y w , Z w ) (X_w,Y_w,Z_w) (Xw​,Yw​,Zw​)表示 2&#xff0c;相机坐标系 相机本身具有一个坐标系&…

嵌入式知识点总结 C/C++ 专题提升(一)-关键字

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.C语言宏中"#“和"##"的用法 1.1.(#)字符串化操作符 1.2.(##)符号连接操作符 2.关键字volatile有什么含意?并举出三个不同的例子? 2.1.并行设备的硬件寄存…

重塑商业智能:大数据改变商业的十种方式

在过去几年间&#xff0c;大数据一直在改变许多公司的运营方式。大数据指的是大量且多样的数据集&#xff0c;当这些数据被妥善收集和分析时&#xff0c;人们能够从中获取有价值的洞察信息。随着大数据逐渐应用于中小型企业&#xff0c;它有望彻底变革企业运营模式。以下将介绍…

基于Spring Boot的车间调度管理系统

基于 Spring Boot 的车间调度管理系统 一、系统概述 基于 Spring Boot 的车间调度管理系统是一个为制造企业车间生产活动提供智能化调度和管理解决方案的软件系统。它利用 Spring Boot 框架的便捷性和高效性&#xff0c;整合车间内的人员、设备、物料、任务等资源&#xff0c…

Ubuntu 24.04 LTS 安装 tailscale 并访问 SMB共享文件夹

Ubuntu 24.04 LTS 安装 tailscale 安装 Tailscale 官方仓库 首先&#xff0c;确保系统包列表是最新的&#xff1a; sudo apt update接下来&#xff0c;安装 Tailscale 所需的仓库和密钥&#xff1a; curl -fsSL https://tailscale.com/install.sh | sh这会自动下载并安装 …

Ubuntu 22.04 TLS 忘记root密码,重启修改的解决办法

1.想办法进入这个界面&#xff0c;我这里是BIOS引导的是按Esc按一下就行&#xff0c;UEFI的貌似是按Shift不得而知&#xff0c;没操作过。下移到Advanced options for Ubuntu&#xff0c;按enter 2.根据使用的内核版本&#xff0c;选择带「recovery mode」字样的内核版本&#…

故障诊断 | BWO白鲸算法优化KELM故障诊断(Matlab)

目录 效果一览文章概述BWO白鲸算法优化KELM故障诊断一、引言1.1、研究背景及意义1.2、故障诊断技术的现状1.3、研究目的与内容二、KELM基本理论2.1、KELM模型简介2.2、核函数的选择2.3、KELM在故障诊断中的应用三、BWO白鲸优化算法3.1、BWO算法基本原理3.2、BWO算法的特点3.3、…

TCP状态转移图详解

状态 描述 LISTEN represents waiting for a connection request from any remote TCP and port. SYN-SENT represents waiting for a matching connection request after having sent a connection request. SYN-RECEIVED represents waiting for a confirming connect…

LabVIEW 水电站厂内经济运行系统

基于 LabVIEW 的水电站经济运行系统&#xff0c;主要针对农村小水电站运行管理的不足进行改进&#xff0c;通过精确控制发电与用水量&#xff0c;最小化耗水量并优化负荷分配&#xff0c;提升水电站的运营效率和经济效益。 ​ LabVIEW 在系统中的功能特点 强大的图形化编程环…

蓝桥杯训练—矩形面积交

文章目录 一、题目二、示例三、解析四、代码 一、题目 平面上有两个矩形&#xff0c;它们的边平行于直角坐标系的X轴或Y轴&#xff0c;对于每个矩形&#xff0c;我们给出它的一对相对顶点的坐标&#xff0c;请你编程写出两个矩形的交的面积 输入格式&#xff1a; 输入包含两行…