Fast Framework

作者 Mr-zhong

开源项目地址 https://github.com/China-Mr-zhong/Fast.Framework

QQ交流群 954866406 欢迎小伙伴加入交流探讨技术

一、前言

Fast Framework 是一个基于NET6.0 封装的轻量级 ORM 框架 支持多种数据库 SqlServer Oracle MySql PostgreSql Sqlite

优点: 体积小、可动态切换不同实现类库、原生支持微软特性、流畅API、使用简单、性能高、模型数据绑定采用 委托+缓存、强大的表达式解析、子查询的原生支持、复杂表达式含成员变量解析,解析性能是目前常见框架中 No1 主要是有缓存的支持 、源代码可读性强。

缺点:目前仅支持Db Frist Code Frist 暂时不考虑 主要是需要花费大量时间和精力。

二、项目明细
名称 说明
Fast.Framework 接口实现类库(框架核心接口实现)
Fast.Framework.Aop Aop类库(基于微软DispatchProxy抽象类封装)
Fast.Framework.Extensions 扩展类库(主要扩展框架核心方法,方便使用)
Fast.Framework.Interfaces 接口类库(框架核心接口定义)
Fast.Framework.Logging 日志类库(主要实现自定义文件日志)
Fast.Framework.Models 模型 框架所用到的实体类
Fast.Framework.Utils 工具类库
Fast.Framework.Test 控制台终端测试项目
Fast.Framework.UnitTest 单元测试项目
Fast.Framework.Web.Test Web测试项目
三、核心对象
  • Ado 原生Ado对象
IAdo ado = new AdoProvider(new DbOptions()
{
DbId = "1",
DbType = DbType.MySQL,
ProviderName = "MySqlConnector",
FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
});
  • DbContext 支持多租户 支持切换不同Ado实现类库 设置 ProviderName和FactoryName 即可
IDbContext db = new DbContext(new List<DbOptions>() {
new DbOptions()
{
DbId = "1",
DbType = DbType.MySQL,
ProviderName = "MySqlConnector",
FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
}});

依赖注入

// 注册服务
builder.Services.AddScoped<IDbContext, DbContext>(); // 数据库选项支持Options接口注入 不是很理解的可以看代码实现
builder.Services.Configure<List<DbOptions>>(configuration.GetSection("DbConfig")); // 产品服务类 通过构造方法注入
public class ProductService
{
/// <summary>
/// 数据库
/// </summary>
private readonly IDbContext db; /// <summary>
/// 构造方法
/// </summary>
/// <param name="db">数据库</param>
public ProductService(IDbContext db)
{
this.db = db;
}
}
四、插入
  • 实体对象插入
var product = new Product()
{
ProductCode = "1001",
ProductName = "测试商品1"
};
var result = await db.Insert(product).ExceuteAsync();
Console.WriteLine($"实体对象插入 受影响行数 {result}");
  • 实体对象插入并返回自增ID 仅支持 SQLServer MySQL SQLite
var product = new Product()
{
ProductCode = "1001",
ProductName = "测试产品1"
};
var result = await db.Insert(product).ExceuteReturnIdentityAsync();
Console.WriteLine($"实体对象插入 返回自增ID {result}");
  • 实体对象列表插入
var list = new List<Product>();
for (int i = 0; i < 2100; i++)
{
list.Add(new Product()
{
ProductCode = $"编号{i + 1}",
ProductName = $"名称{i + 1}"
});
}
var result = await db.Insert(list).ExceuteAsync();
Console.WriteLine($"实体对象列表插入 受影响行数 {result}");
  • 匿名对象插入
var obj = new
{
ProductCode = "1001",
ProductName = "测试商品1"
};
//注意:需要使用As方法显示指定表名称
var result = await db.Insert(obj).As("product").ExceuteAsync();
Console.WriteLine($"匿名对象插入 受影响行数 {result}");
  • 匿名对象列表插入
var list = new List<object>();
for (int i = 0; i < 2100; i++)
{
list.Add(new
{
ProductCode = $"编号{i + 1}",
ProductName = $"名称{i + 1}"
});
}
//注意:需要使用As方法显示指定表名称
var result = await db.Insert(list).As("Product").ExceuteAsync();
Console.WriteLine($"匿名对象列表插入 受影响行数 {result}");
  • 字典插入
var product = new Dictionary<string, object>()
{
{"ProductCode","1001"},
{ "ProductName","测试商品1"}
};
//注意:需要显示指定类型否则无法重载到正确的方法,如果没有实体类型可用object类型并配合As方法显示指定表名称.
var result = await db.Insert<Product>(product).ExceuteAsync();
Console.WriteLine($"字典插入 受影响行数 {result}");
  • 字典列表插入
var list = new List<Dictionary<string, object>>();
for (int i = 0; i < 2100; i++)
{
list.Add(new Dictionary<string, object>()
{
{"ProductCode","1001"},
{ "ProductName","测试商品1"}
});
}
//注意:需要显示指定泛型类型否则无法重载到正确的方法,如果没有实体可用object类型并配合As方法显示指定表名称.
var result = await db.Insert<Product>(list).ExceuteAsync();
Console.WriteLine($"字典列表插入 受影响行数 {result}");
五、删除
  • 实体对象删除
var product = new Product()
{
ProductId = 1,
ProductCode = "1001",
ProductName = "测试商品1"
};
//注意:必须标记KeyAuttribute特性 否则将抛出异常
var result = await db.Delete(product).ExceuteAsync();
Console.WriteLine($"实体删除 受影响行数 {result}");
  • 无条件删除
var result = await db.Delete<Product>().ExceuteAsync();
Console.WriteLine($"无条件删除 受影响行数 {result}");
  • 表达式删除
var result = await db.Delete<Product>().Where(w => w.ProductId == 1).ExceuteAsync();
Console.WriteLine($"条件删除 受影响行数 {result}");
  • 特殊删除
//特殊用法 如需单个条件或多个可搭配 WhereColumn或WhereColumns方法
var result = await db.Delete<object>().As("Product").ExceuteAsync();
Console.WriteLine($"无实体删除 受影响行数 {result}");
六、更新
  • 实体对象更新
var product = new Product()
{
ProductId = 1,
ProductCode = "1001",
ProductName = "测试商品1"
};
//注意:标记KeyAuttribute特性属性或使用Where条件,为了安全起见全表更新将必须使用Where方法
var result = await db.Update(product).ExceuteAsync();
Console.WriteLine($"对象更新 受影响行数 {result}");
  • 指定列更新
var result = await db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" })
.Columns("ProductCode", "ProductName").ExceuteAsync();
// 字段很多的话可以直接new List<string>(){"列1","列2"}
  • 忽略列更新
var result = await db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" })
.IgnoreColumns("Custom1").ExceuteAsync();
// 同上使用方法一样
  • 实体对象列表更新
var list = new List<Product>();
for (int i = 0; i < 2022; i++)
{
list.Add(new Product()
{
ProductCode = $"编号{i + 1}",
ProductName = $"名称{i + 1}"
});
}
//注意:标记KeyAuttribute特性属性或使用WhereColumns方法指定更新条件列
var result = await db.Update(list).ExceuteAsync();
Console.WriteLine($"对象列表更新 受影响行数 {result}");
  • 匿名对象更新
var obj = new
{
ProductId = 1,
ProductCode = "1001",
ProductName = "测试商品1"
};
//注意:需要显示指定表名称 以及更新条件 使用 Where或者WhereColumns方法均可
var result = await db.Update(obj).As("product").WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"匿名对象更新 受影响行数 {result}");
  • 匿名对象列表更新
var list = new List<object>();
for (int i = 0; i < 2022; i++)
{
list.Add(new
{
ProductId = i + 1,
ProductCode = $"编号{i + 1}",
ProductName = $"名称{i + 1}"
});
}
//由于是匿名对象需要显示指定表名称,使用WhereColumns方法指定更新条件列
var result = await db.Update(list).As("Product").WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"匿名对象列表更新 受影响行数 {result}");
  • 字典更新
var product = new Dictionary<string, object>()
{
{ "ProductId",1},
{"ProductCode","1001"},
{ "ProductName","测试商品1"}
};
//注意:需要显示指定泛型类型否则无法重载到正确的方法并且使用WhereColumns方法指定条件列
var result = await db.Update<Product>(product).WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"字典更新 受影响行数 {result}");
  • 字典列表更新
var list = new List<Dictionary<string, object>>();
for (int i = 0; i < 2022; i++)
{
list.Add(new Dictionary<string, object>()
{
{ "ProductId",i+1},
{"ProductCode",$"更新编号:{i+1}"},
{ "ProductName",$"更新商品:{i + 1}"}
});
}
//注意:需要显示指定泛型类型否则无法重载到正确的方法并且使用WhereColumns方法执行条件列
var result = await db.Update<Product>(list).WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"字典列表更新 受影响行数 {result}");
  • 表达式更新
var product = new Product()
{
ProductId = 1,
ProductCode = "1001",
ProductName = "测试商品1"
};
var result = await db.Update(product).Where(p => p.ProductId == 100).ExceuteAsync();
Console.WriteLine($"表达式更新 受影响行数 {result}");
七、查询
  • 单一查询
var data = await db.Query<Product>().FristAsync();
  • 列表查询
var data = await db.Query<Product>().ToListAsync();
  • 返回单个字典
var data = await db.Query<Product>().ToDictionaryAsync();
  • 返回字典列表
var data = await db.Query<Product>().ToDictionaryListAsync();
  • 分页查询
var page = new Pagination() { Page = 1, PageSize = 100 };
var data = await db.Query<Product>().ToPageListAsync(page);
  • 计数查询
var data = await db.Query<Product>().CountAsync();
  • 任何查询
var data = await db.Query<Product>().AnyAsync();
  • 条件查询
var data = await db.Query<Product>().Where(w => w.ProductId == 1);
//需要调用返回数据结果的方法 例如:ToListAsync
  • Like 查询
var data = await db.Query<Product>().Where(w => w.ProductName.StartsWith("左模糊") || w.ProductName.EndsWith("右模糊") || w.ProductName.Contains("全模糊"));
  • Not Like查询
var data = await db.Query<Product>().Where(w => !w.ProductName.StartsWith("左模糊") || !w.ProductName.EndsWith("右模糊") || !w.ProductName.Contains("全模糊"));

//由于没有专门去扩展 Not Like 方法,可以用取反或使用比较变通实现 例如 w.ProductName.StartsWith("左模糊")==false
//Mysql举例 最终解析后的结果为 `ProductName` Like '%左模糊' = 0 这种用法数据库是支持的 相当于 Not Like
  • Select查询 (选择字段)
var data = await db.Query<Product>().Select(s => new
{
s.ProductId,
s.ProductName
}).ToListAsync();
  • 分组查询
var data = await db.Query<Product>().GroupBy(s => new
{
s.ProductId,
s.ProductName
}).ToListAsync();
  • 分组聚合查询
var sql = db.Query<Order>().InnerJoin<OrderDetail>((a, b) => a.OrderId == b.OrderId).GroupBy((a, b) => new
{
a.OrderCode
}).Select((a, b) => new
{
a.OrderCode,
Sum_Qty = SqlFunc.Sum(b.Qty)//支持嵌套
}).ToListAsync();
  • 排序查询
var data = await db.Query<Product>().OrderBy(s => new
{
s.CreateTime
}).ToListAsync();
//这是多个字段排序使用方法 还有其它重载方法
  • Having查询
var data = await db.Query<Product>().GroupBy(s => new
{
s.ProductId,
s.ProductName
}).Having(s => SqlFunc.Count(s.ProductId) > 1).ToListAsync();
//必须先使用GroupBy方法 懂得都懂
  • 联表查询
var data = await db.Query<Product>().
LeftJoin<Class1>((a, b) => a.ProductId == b.ProductId).ToListAsync();
// 右连接对应的是 RightJoin 内连接对应 InnerJoin
  • 联合查询
var query1 = db.Query<Product>();
var query2 = db.Query<Product>();
db.Union(query1, query2);//联合
db.UnionAll(query1, query2);//全联合
//执行查询调用Toxx方法
  • 查询并插入 仅支持同实例的数据库 跨库 个人还是建议 用事务分开写查询和插入
//方式1
var result1 = await db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
{
s.ProductCode,
s.ProductName
}).Insert<Product>(p => new
{
p.ProductCode,
p.ProductName
}); //方式2 需要注意的是 显示指定不带 列标识符 例如 `列名称1` 如有字段冲突 可自行加上标识符
var result2 = await db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
{
s.ProductCode,
s.ProductName
}).Insert("表名称 同实例不同库 可以使用 db.数据库名称.表名称 ", "列名称1", "列名称2", "`带标识的列名称3`"); //方式3 需要注意同方式2 一样
var result3 = await db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
{
s.ProductCode,
s.ProductName
}).Insert("表名称 同实例不同库 可以使用 db.数据库名称.表名称 ", new List<string>() { "列名称1" });
  • In查询
// 方式1
var data1 = await db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, "1001", "1002")).ToListAsync(); // 方式2
var data2 = await db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, new List<string>() { "123", "456" })).ToListAsync(); // 方式3 需要动态更新IN值 使用这种
var list = new List<string>() { "123", "456" };
var data3 = await db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, list)).ToListAsync(); // 方法4 参数同上一样 单独分离IN和NotIN 是为了兼容匿名查询
var data4 = await db.Query<Product>().In("字段名称", "1001", "1002").ToListAsync();
  • 子查询
var subQuery = db.Query<Product>().Where(w => w.ProductId == 1).Select(s => s.ProductName);
var sql1 = db.Query<Product>().Select(s => new Product()
{
Custom1 = db.SubQuery<string>(subQuery)// SubQuery 的泛型是根据你左边赋值的属性类型来定义
}).ToListAsync();
// 这种没有使用new 的 泛型可随意定义 实际作用就是避免 对象属性赋值类型冲突的问题
var sql2 = db.Query<Product>().Select(s => db.SubQuery<string>(subQuery)).ToListAsync();
八、Lambda表达式
  • 高性能表达式动态缓存的支持
var list = new List<string>() { "1001" };
Expression<Func<Product, bool>> ex = p => SqlFunc.In(p.ProductCode, list); for (int i = 1; i <= 3; i++)
{
list.Add($"动态添加参数{i}");
var stopwatch1 = new Stopwatch();
stopwatch1.Start();
var result = ex.ResolveSql(new ResolveSqlOptions()
{
DbType = Models.DbType.MySQL,
ResolveSqlType = ResolveSqlType.Where
});
stopwatch1.Stop();
Console.WriteLine($"解析耗时:{stopwatch1.ElapsedMilliseconds}ms {stopwatch1.ElapsedMilliseconds / 1000.00}s 解析Sql字符串:{result.SqlString}");
}
  • 解析结果
解析耗时:14ms 0.014s 解析Sql字符串:p.`ProductCode` IN ( @2dac7a1c4aa64036aeee858b86fbd3a4_0,@2dac7a1c4aa64036aeee858b86fbd3a4_1 )
解析耗时:0ms 0s 解析Sql字符串:p.`ProductCode` IN ( @3b6b8fcb2f674cf490d44f97525c3c2b_0,@3b6b8fcb2f674cf490d44f97525c3c2b_1,@3b6b8fcb2f674cf490d44f97525c3c2b_2 )
解析耗时:0ms 0s 解析Sql字符串:p.`ProductCode` IN ( @4447c5d65e8a49c9b04549b7aac868b2_0,@4447c5d65e8a49c9b04549b7aac868b2_1,@4447c5d65e8a49c9b04549b7aac868b2_2,@4447c5d65e8a49c9b04549b7aac868b2_3 )
  • 动态表达式
var ex = DynamicWhereExp.Create<Product>().AndIF(1 == 1, a => a.DeleteMark == true).Build();
var data =await db.Query<Product>().Where(ex).ToListAsync();
九、数据库日志
db.Aop.DbLog = (sql, dp) =>
{
Console.WriteLine($"执行Sql:{sql}");
if (dp != null)
{
foreach (var item in dp)
{
Console.WriteLine($"参数名称:{item.ParameterName} 参数值:{item.Value}");
}
}
};
十、事务
  • 普通事务
try
{
await db.Ado.BeginTranAsync();//开启事务 // 执行 CRUD await db.Ado.CommitTranAsync();//提交事务
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
await db.Ado.RollbackTranAsync();//回滚事务
}
  • 更大范围的事务 使用微软 TransactionScope 对象
using (var tran = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
// 执行你的增删改查
// 可使用原生Ado或DbContext对象的CURD方法
tran.Complete();//提交事务
}
十一、多租户
  • 改变数据库
//数据库配置可从Json配置文件加载
IDbContext db = new DbContext(new List<DbOptions>() {
new DbOptions()
{
DbId = "0",
DbType = Models.DbType.SQLServer,
ProviderName = "System.Data.SqlClient",
FactoryName = "System.Data.SqlClient.SqlClientFactory,System.Data",
ConnectionStrings = "server=localhost;database=Test;user=sa;pwd=123456789;min pool size=3;max pool size=100;connect timeout=30;"
},
new DbOptions()
{
DbId = "1",
DbType = Models.DbType.MySQL,
ProviderName = "MySqlConnector",
FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
}});
db.ChangeDb("1");//切换到MySQL
十二、原生特性支持
/// <summary>
/// 产品
/// </summary>
[Table("ProductMain")]
public class Product
{
/// <summary>
/// 产品ID
/// </summary>
[Key]
public int ProductId { get; set; } /// <summary>
/// 产品编号
/// </summary>
[Column("ProductCode")]//不标记默认取当前属性名称
public string ProductCode { get; set; } /// <summary>
/// 自定义1
/// </summary>
[NotMapped]
public string Custom1 { get; set; }
}
十三、原生Ado使用
// 原始起步
// var conn = db.Ado.DbProviderFactory.CreateConnection();
// var cmd = conn.CreateCommand(); // 封装的方法分别以Execute和Create开头以及预处理 PrepareCommand 方法
// 该方法可以自动帮你处理执行的预操作,主要作用是代码复用。 // 当有非常复杂的查询 ORM不能满足需求的时候可以使用原生Ado满足业务需求 // 构建数据集核心扩展方法 分别有 FristBuildAsync ListBuildAsync DictionaryBuildAsync DictionaryListBuildAsync
var data = await db.Ado.ExecuteReaderAsync(CommandType.Text, "select * from product", null).ListBuildAsync<Product>();

Fast.Framework ORM 于中秋节后 正式开源的更多相关文章

  1. Fast.Framework ORM 试用

    简介 Fast.Framework 是一款基于 .NET 6 封装的轻量级ORM框架,支持多种数据库(SQL Server.Oracle.MySQL.PostgreSQL.SQLite). 优点 性能 ...

  2. Restful.Data v1.0 - 轻量级数据持久层组件, 正式开源发布了

    经过几个星期的优化调整,今天 Restful.Data 正式开源发布. 源码地址:https://github.com/linli8/Restful 今天不写那么多废话了,还是重新介绍一下 Restf ...

  3. 基于NET 6.0 封装的 Fast.Framework

    Fast Framework 项目地址 https://gitee.com/China-Mr-zhong/Fast.Framework Author Mr-zhong Wechat 850856667 ...

  4. 如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源

    1.前言 关于微信内部正在使用的网络层封装库Mars开源的消息,1个多月前就已满天飞(参见<微信Mars:微信内部正在使用的网络层封装库,即将开源>),不过微信团队没有失约,微信Mars ...

  5. 【转】如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源

    网上看到关于微信官方的跨平台跨业务的终端基础组件Mars的介绍文章,转载这这里.源代码: https://github.com/Tencent/mars作者:男人链接:https://zhuanlan ...

  6. 中文编程语言Z语言开源正式开源!!!

    (Z语言基于.NET环境,源码中有很多高技术的代码,让更多的人知道对大家有会有很好的帮助,请管理员一点要批准放在首页) 本人实现的中文编程语言Z语言现在正式开源,采用LGPL协议. 编译器核心的网址为 ...

  7. fir.im Log Guru 正式开源,快速找到 iOS 应用无法安装的原因

    很开心的宣布 Log Guru 正式开源! Log Guru,是 fir.im 开发团队创造的小轮子,用在 Mac 电脑上的日志获取,Github 地址:FIRHQ/LogGuru. Log Guru ...

  8. Swift 正式开源, 包括 Swift 核心库和包管理器

    Swift 正式开源!Swift 团队很高兴宣布 Swift 开始开源新篇章.自从苹果发布 Swfit 编程语言,就成为了历史上发展最快的编程语言之一.Swift 通过设计使得软件编写更加快速更加安全 ...

  9. 微软.NET Framework 4.5.2 RTM正式版

    今天,微软.NET开发团队发布.NET Framework 4.5.2 RTM正式版.新版框架继续高度兼容现有的.NET Framework 4.4.5.4.5.1等版本,该版本框架与旧版的.NET ...

随机推荐

  1. label问题排查:打不开标注好的图像

    问题描述 之前标注好的文件,标注有bbox和若干points.选择Open Dir打开图像目录,选择Change Output Dir选择json文件所在目录.发现有些图片能正常显示标注后的状态.而有 ...

  2. Java中StringBuffer 简单学习,LeetCode中1323题运用

    StringBuffer 学习 StringBuffer() 构造一个没有字符的字符串缓冲区,初始容量为16个字符. deleteCharAt(int index) 删除char在这个指定序列inde ...

  3. Vue模板解析

    mustcache 什么是模板引擎 模板引擎是将数据变为视图的最优雅的解决方案 数据 [ {"name":"小明","age":12,&qu ...

  4. JDBCToolsV3 :DAO

    编写文件和步骤 ①,bean模块:数据类Course,包含数据的class,封装数据类型; ②,DAO:1)定义对数据的操作接口,及规定标准(包含怎样的操作).例如:CourseDAO数据库操作的接口 ...

  5. Ubuntu安装python各版本

    编译安装的话,之前遇到过很多小问题,感觉还是通过添加这个ppa方式装的比较稳,缺点是可能安装的比较慢,可配合proxychain4 sudo apt install software-properti ...

  6. $\mathcal{A\,F\,O}$

    突然间,告诉我不用学计算机了 真的有点像是做梦 回忆自己的OI生涯 真的不知从何说起 小学的时候 被家长哄着报名了当时很热门的 logo语言 在炎热的夏天,电脑里小海龟的步伐从未停歇 那时的自己很骄傲 ...

  7. 吐泡泡_via牛客网

    题目 链接:https://ac.nowcoder.com/acm/contest/28537/E 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言 ...

  8. AtCoder Beginner Contest 260 F - Find 4-cycle

    题目传送门:F - Find 4-cycle (atcoder.jp) 题意: 给定一个无向图,其包含了S.T两个独立点集(即S.T内部间的任意两点之间不存在边),再给出图中的M条边(S中的点与T中的 ...

  9. 第十九天python3 json和messagepack

    json的数据类型官网:https://www.json.org/ 值: 双引号内的字符串,数值,true和false,null,对象,数组:字符串: 双引号内的任意字符的组合,可以有专一字符:数值: ...

  10. 第十八天python3 序列化和反序列化

    思考: 内存中的字典.列表.集合以及各种对象,如何保存到一个文件中? 如果是自己定义的类的实例,如何保存到一个文件中? 如何从文件中读取数据,并让它们在内存中再次变成自己对应的类的实例? 要设计一套协 ...