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. Tapdata 实时数据融合平台解决方案(一):现代企业数据架构及痛点

    作者介绍:TJ,唐建法,Tapdata 钛铂数据 CTO,MongoDB中文社区主席,原MongoDB大中华区首席架构师,极客时间MongoDB视频课程讲师. "怎样可以来搭建一个数据中台? ...

  2. 如何编写测试团队通用的Jmeter脚本

    平时学习.工作过程中,编写的一些jmeter脚本,相信大多数都遇到过这个问题.那就是:如果换一台电脑运行,文件路径不一样,会导致运行失败. 前不久,自己就真真切切遇到过一回,A同学写了个脚本用于压测, ...

  3. day11 Java反射机制

    java反射机制 反射是java中的动态机制,它允许我们在程序运行期间再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定. 因此,反射可以大大的提高代码的灵活度,但是随之而来 ...

  4. Solution -「SDOI2011」拦截导弹

    Sol.   题目要求一个数对序列的二维最长下降子序列,我们称其为 Q.并求出每一个元素分别在可能的 Q 中出现了多少次.   直接 Dp,时间复杂度 \(O(n^2)\) 不行.考虑 CDQ 分治 ...

  5. 基础算法学习以及$STL$的使用

    1.优先队列 (1)大根堆(小顶堆) priority_queue<int,vector<int>,greater<int> >q; (2)小根堆(大顶堆) pri ...

  6. 日志审计与分析实验三(rsyslog服务器端和客户端配置)(Linux日志收集)

    Linux日志收集 一.实验目的: 1.掌握rsyslog配置方法 2.配置rsyslog服务收集其他Linux服务器日志: C/S架构:客户端将其日志上传到服务器端,通过对服务器端日志的查询,来实现 ...

  7. Java基础语法02

    回顾前面的章节,我们学习了(1.注释,2.标识符和关键字,3.数据类型)今天让我们继续加油. 四.变量,常量,作用域1.变量是什么:存数的(可以变化的量) Java是一种强类型语言,每个变量都必须声明 ...

  8. linux 安装 apache+mysql+php

    http://www.cnblogs.com/lufangtao/archive/2012/12/30/2839679.html

  9. ExcelPatternTool: Excel表格-数据库互导工具

    ExcelPatternTool Excel表格-数据库互导工具 介绍: 指定Pattern文件-一个规则描述的json文档,基于此规则实现Excel表格与数据库之间的导入导出,校验等功能. 特点: ...

  10. RSS订阅微信公众号初探-feed43

    为什么用RSS,能怎么用RSS订阅微信公众号 建议信息聚合(Really Simple Syndication, RSS)在08年我第一次摸到自己家电脑时就给我留下了印象,当时还想这打开都啥玩意呀怎么 ...