【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 (15)源码分享一:修改仓储IRepository、RepositoryBase
索引
【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 目录索引
简述
前言:
从今天开始,我们逐步把修正前后的源码分享给大家,还是百度网盘分享,百度网盘只发两次,两次都失效后,请大家互相传递。
数据库,已经在上一章发布完成的项目中给大家了,这里不再重复发送数据库。
今天,分享的是修改前后的仓储IRepository、RepositoryBase
修改前弊端:
如果大家仔细看过之前的仓储IRepository、RepositoryBase,那么大家肯定会发现,这个类无论是在效率上还是逻辑上,还是有很多不合适、不合理的。我本来是想在 【无私分享:ASP.NET CORE 项目实战】目录索引 系列中逐步修改,但是经过再三考虑,如果把这个源码直接给大家,对于刚接触的朋友实在是太不负责任,容易产生误导,所以,还是要在这里进行修正。
在【无私分享:ASP.NET CORE 项目实战(第五章)】Repository仓储 UnitofWork 这篇文章中,简单介绍了一下个人对于UnitOfWrok的理解,虽然 EF 是自带事务的,但是我们的 Context.SaveChanges() 无疑抛弃了统一的事务,同时,作为一个封装的仓储类,我们是应该提供 串行和并行 两种方式的,至于到底用不用异步方式,这个我们不能泛滥,异步不是没有损耗的,我们只是提供两种方式,到底用什么,要根据业务需要调用,而不是一味的异步方式,所有的东西都是过无不及。
修正一:添加UnitOfWork
这是我们之前的Service 类库:
我们添加 IUnitOfWork 接口 和 UnitOfWork 实现类
1 namespace Service 2 { 3 public interface IUnitOfWork 4 { 5 /// <summary> 6 /// 提交 7 /// </summary> 8 /// <returns></returns> 9 bool Commit(); 10 16 } 17 }
1 public class UnitOfWork : IUnitOfWork, IDisposable 2 { 3 #region 数据上下文 4 5 private DbContext context = new MyConfig().db; 6 /// <summary> 7 /// 数据上下文 8 /// </summary> 9 public DbContext _Context 10 { 11 get 12 { 13 context.Configuration.ValidateOnSaveEnabled = false; 14 return context; 15 } 16 } 17 18 #endregion 19 20 /// <summary> 21 /// 提交 22 /// </summary> 23 /// <returns></returns> 24 public bool Commit() 25 { 26 return _Context.SaveChanges() > 0; 27 } 28 29 public void Dispose() 30 { 31 if (_Context != null) 32 { 33 _Context.Dispose(); 34 } 35 GC.SuppressFinalize(this); 36 } 37 }
修正二:修改IRepository
首先,把涉及到事务的没用都删掉:
修改单模型CRUD操作:①、增加异步方式 ②、添加 bool IsCommit = true 参数,判断是否提交
这个参数的主要作用是,我们在进行单项操作的时候,可以直接提交保存,当进行多项操作的时候,我们这个传入False,然后 通过 UnitOfWork 统一提交保存
1 #region 单模型 CRUD 操作 2 /// <summary> 3 /// 增加一条记录 4 /// </summary> 5 /// <param name="entity">实体模型</param> 6 /// <param name="IsCommit">是否提交(默认提交)</param> 7 /// <returns></returns> 8 bool Save(T entity, bool IsCommit = true); 9 /// <summary> 10 /// 增加一条记录(异步方式) 11 /// </summary> 12 /// <param name="entity">实体模型</param> 13 /// <param name="IsCommit">是否提交(默认提交)</param> 14 /// <returns></returns> 15 Task<bool> SaveAsync(T entity, bool IsCommit = true); 16 17 /// <summary> 18 /// 更新一条记录 19 /// </summary> 20 /// <param name="entity">实体模型</param> 21 /// <param name="IsCommit">是否提交(默认提交)</param> 22 /// <returns></returns> 23 bool Update(T entity, bool IsCommit = true); 24 /// <summary> 25 /// 更新一条记录(异步方式) 26 /// </summary> 27 /// <param name="entity">实体模型</param> 28 /// <param name="IsCommit">是否提交(默认提交)</param> 29 /// <returns></returns> 30 Task<bool> UpdateAsync(T entity, bool IsCommit = true); 31 32 /// <summary> 33 /// 增加或更新一条记录 34 /// </summary> 35 /// <param name="entity">实体模型</param> 36 /// <param name="IsSave">是否增加</param> 37 /// <param name="IsCommit">是否提交(默认提交)</param> 38 /// <returns></returns> 39 bool SaveOrUpdate(T entity, bool IsSave, bool IsCommit = true); 40 /// <summary> 41 /// 增加或更新一条记录(异步方式) 42 /// </summary> 43 /// <param name="entity">实体模型</param> 44 /// <param name="IsSave">是否增加</param> 45 /// <param name="IsCommit">是否提交(默认提交)</param> 46 /// <returns></returns> 47 Task<bool> SaveOrUpdateAsync(T entity, bool IsSave, bool IsCommit = true); 48 49 #endregion
修改多模型操作,同时增加异步方式:
1 #region 多模型操作 2 /// <summary> 3 /// 增加多条记录,同一模型 4 /// </summary> 5 /// <param name="T1">实体模型集合</param> 6 /// <param name="IsCommit">是否提交(默认提交)</param> 7 /// <returns></returns> 8 bool SaveList(List<T> T1, bool IsCommit = true); 9 /// <summary> 10 /// 增加多条记录,同一模型(异步方式) 11 /// </summary> 12 /// <param name="T1">实体模型集合</param> 13 /// <param name="IsCommit">是否提交(默认提交)</param> 14 /// <returns></returns> 15 Task<bool> SaveListAsync(List<T> T1, bool IsCommit = true); 16 17 /// <summary> 18 /// 增加多条记录,独立模型 19 /// </summary> 20 /// <param name="T1">实体模型集合</param> 21 /// <param name="IsCommit">是否提交(默认提交)</param> 22 /// <returns></returns> 23 bool SaveList<T1>(List<T1> T, bool IsCommit = true) where T1 : class; 24 /// <summary> 25 /// 增加多条记录,独立模型(异步方式) 26 /// </summary> 27 /// <param name="T1">实体模型集合</param> 28 /// <param name="IsCommit">是否提交(默认提交)</param> 29 /// <returns></returns> 30 Task<bool> SaveListAsync<T1>(List<T1> T, bool IsCommit = true) where T1 : class; 31 32 /// <summary> 33 /// 更新多条记录,同一模型 34 /// </summary> 35 /// <param name="T1">实体模型集合</param> 36 /// <param name="IsCommit">是否提交(默认提交)</param> 37 /// <returns></returns> 38 bool UpdateList(List<T> T1, bool IsCommit = true); 39 /// <summary> 40 /// 更新多条记录,同一模型(异步方式) 41 /// </summary> 42 /// <param name="T1">实体模型集合</param> 43 /// <param name="IsCommit">是否提交(默认提交)</param> 44 /// <returns></returns> 45 Task<bool> UpdateListAsync(List<T> T1, bool IsCommit = true); 46 47 /// <summary> 48 /// 更新多条记录,独立模型 49 /// </summary> 50 /// <param name="T1">实体模型集合</param> 51 /// <param name="IsCommit">是否提交(默认提交)</param> 52 /// <returns></returns> 53 bool UpdateList<T1>(List<T1> T, bool IsCommit = true) where T1 : class; 54 /// <summary> 55 /// 更新多条记录,独立模型(异步方式) 56 /// </summary> 57 /// <param name="T1">实体模型集合</param> 58 /// <param name="IsCommit">是否提交(默认提交)</param> 59 /// <returns></returns> 60 Task<bool> UpdateListAsync<T1>(List<T1> T, bool IsCommit = true) where T1 : class; 61 62 /// <summary> 63 /// 删除多条记录,同一模型 64 /// </summary> 65 /// <param name="T1">实体模型集合</param> 66 /// <param name="IsCommit">是否提交(默认提交)</param> 67 /// <returns></returns> 68 bool DeleteList(List<T> T1, bool IsCommit = true); 69 /// <summary> 70 /// 删除多条记录,同一模型(异步方式) 71 /// </summary> 72 /// <param name="T1">实体模型集合</param> 73 /// <param name="IsCommit">是否提交(默认提交)</param> 74 /// <returns></returns> 75 Task<bool> DeleteListAsync(List<T> T1, bool IsCommit = true); 76 77 /// <summary> 78 /// 删除多条记录,独立模型 79 /// </summary> 80 /// <param name="T1">实体模型集合</param> 81 /// <param name="IsCommit">是否提交(默认提交)</param> 82 /// <returns></returns> 83 bool DeleteList<T1>(List<T1> T, bool IsCommit = true) where T1 : class; 84 /// <summary> 85 /// 删除多条记录,独立模型(异步方式) 86 /// </summary> 87 /// <param name="T1">实体模型集合</param> 88 /// <param name="IsCommit">是否提交(默认提交)</param> 89 /// <returns></returns> 90 Task<bool> DeleteListAsync<T1>(List<T1> T, bool IsCommit = true) where T1 : class; 91 92 /// <summary> 93 /// 通过Lamda表达式,删除一条或多条记录 94 /// </summary> 95 /// <param name="predicate"></param> 96 /// <param name="IsCommit"></param> 97 /// <returns></returns> 98 bool Delete(Expression<Func<T, bool>> predicate, bool IsCommit = true); 99 /// <summary> 100 /// 通过Lamda表达式,删除一条或多条记录(异步方式) 101 /// </summary> 102 /// <param name="predicate"></param> 103 /// <param name="IsCommit"></param> 104 /// <returns></returns> 105 Task<bool> DeleteAsync(Expression<Func<T, bool>> predicate, bool IsCommit = true); 106 107 /// <summary> 108 /// 执行SQL删除 109 /// </summary> 110 /// <param name="sql">SQL语句</param> 111 /// <param name="para">Parameters参数</param> 112 int DeleteBySql(string sql, params DbParameter[] para); 113 /// <summary> 114 /// 执行SQL删除(异步方式) 115 /// </summary> 116 /// <param name="sql">SQL语句</param> 117 /// <param name="para">Parameters参数</param> 118 Task<int> DeleteBySql(string sql, params DbParameter[] para); 119 #endregion
修改获取多条数据操作,同时增加异步方式:
1 #region 获取多条数据操作 2 3 /// <summary> 4 /// 返回IQueryable集合,延时加载数据 5 /// </summary> 6 /// <param name="predicate"></param> 7 /// <returns></returns> 8 IQueryable<T> LoadAll(Expression<Func<T, bool>> predicate); 9 /// <summary> 10 /// 返回IQueryable集合,延时加载数据(异步方式) 11 /// </summary> 12 /// <param name="predicate"></param> 13 /// <returns></returns> 14 Task<IQueryable<T>> LoadAllAsync(Expression<Func<T, bool>> predicate); 15 16 // <summary> 17 /// 返回List<T>集合,不采用延时加载 18 /// </summary> 19 /// <param name="predicate"></param> 20 /// <returns></returns> 21 List<T> LoadListAll(Expression<Func<T, bool>> predicate); 22 // <summary> 23 /// 返回List<T>集合,不采用延时加载(异步方式) 24 /// </summary> 25 /// <param name="predicate"></param> 26 /// <returns></returns> 27 Task<List<T>> LoadListAllAsync(Expression<Func<T, bool>> predicate); 28 29 /// <summary> 30 /// 获取DbQuery的列表 31 /// </summary> 32 /// <param name="predicate"></param> 33 /// <returns></returns> 34 DbQuery<T> LoadQueryAll(Expression<Func<T, bool>> predicate); 35 /// <summary> 36 /// 获取DbQuery的列表(异步方式) 37 /// </summary> 38 /// <param name="predicate"></param> 39 /// <returns></returns> 40 Task<DbQuery<T>> LoadQueryAllAsync(Expression<Func<T, bool>> predicate); 41 42 /// <summary> 43 /// 获取IEnumerable列表 44 /// </summary> 45 /// <param name="sql">SQL语句</param> 46 /// <param name="para">Parameters参数</param> 47 /// <returns></returns> 48 IEnumerable<T> LoadEnumerableAll(string sql,params DbParameter[] para); 49 /// <summary> 50 /// 获取IEnumerable列表(异步方式) 51 /// </summary> 52 /// <param name="sql">SQL语句</param> 53 /// <param name="para">Parameters参数</param> 54 /// <returns></returns> 55 Task<IEnumerable<T>> LoadEnumerableAllAsync(string sql, params DbParameter[] para); 56 57 /// <summary> 58 /// 获取数据动态集合 59 /// </summary> 60 /// <param name="sql">SQL语句</param> 61 /// <param name="para">Parameters参数</param> 62 /// <returns></returns> 63 IEnumerable LoadEnumerable(string sql, params DbParameter[] para); 64 /// <summary> 65 /// 获取数据动态集合(异步方式) 66 /// </summary> 67 /// <param name="sql">SQL语句</param> 68 /// <param name="para">Parameters参数</param> 69 /// <returns></returns> 70 Task<IEnumerable> LoadEnumerableAsync(string sql, params DbParameter[] para); 71 72 /// <summary> 73 /// 采用SQL进行数据的查询,返回IList集合 74 /// </summary> 75 /// <param name="sql">SQL语句</param> 76 /// <param name="para">Parameters参数</param> 77 /// <returns></returns> 78 List<T> SelectBySql(string sql, params DbParameter[] para); 79 /// <summary> 80 /// 采用SQL进行数据的查询,返回IList集合(异步方式) 81 /// </summary> 82 /// <param name="sql">SQL语句</param> 83 /// <param name="para">Parameters参数</param> 84 /// <returns></returns> 85 Task<List<T>> SelectBySqlAsync(string sql, params DbParameter[] para); 86 87 /// <summary> 88 /// 采用SQL进行数据的查询,指定泛型,返回IList集合 89 /// </summary> 90 /// <typeparam name="T1"></typeparam> 91 /// <param name="sql"></param> 92 /// <param name="para"></param> 93 /// <returns></returns> 94 List<T1> SelectBySql<T1>(string sql, params DbParameter[] para); 95 /// <summary> 96 /// 采用SQL进行数据的查询,指定泛型,返回IList集合 97 /// </summary> 98 /// <typeparam name="T1"></typeparam> 99 /// <param name="sql"></param> 100 /// <param name="para"></param> 101 /// <returns></returns> 102 Task<List<T1>> SelectBySqlAsync<T1>(string sql, params DbParameter[] para); 103 104 /// <summary> 105 /// 可指定返回结果、排序、查询条件的通用查询方法,返回实体对象集合 106 /// </summary> 107 /// <typeparam name="TEntity">实体对象</typeparam> 108 /// <typeparam name="TOrderBy">排序字段类型</typeparam> 109 /// <typeparam name="TResult">数据结果,与TEntity一致</typeparam> 110 /// <param name="where">过滤条件,需要用到类型转换的需要提前处理与数据表一致的</param> 111 /// <param name="orderby">排序字段</param> 112 /// <param name="selector">返回结果(必须是模型中存在的字段)</param> 113 /// <param name="IsAsc">排序方向,true为正序false为倒序</param> 114 /// <returns>实体集合</returns> 115 List<TResult> QueryEntity<TEntity, TOrderBy, TResult>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TOrderBy>> orderby, Expression<Func<TEntity, TResult>> selector, bool IsAsc) 116 where TEntity : class 117 where TResult : class; 118 /// <summary> 119 /// 可指定返回结果、排序、查询条件的通用查询方法,返回实体对象集合(异步方式) 120 /// </summary> 121 /// <typeparam name="TEntity">实体对象</typeparam> 122 /// <typeparam name="TOrderBy">排序字段类型</typeparam> 123 /// <typeparam name="TResult">数据结果,与TEntity一致</typeparam> 124 /// <param name="where">过滤条件,需要用到类型转换的需要提前处理与数据表一致的</param> 125 /// <param name="orderby">排序字段</param> 126 /// <param name="selector">返回结果(必须是模型中存在的字段)</param> 127 /// <param name="IsAsc">排序方向,true为正序false为倒序</param> 128 /// <returns>实体集合</returns> 129 Task<List<TResult>> QueryEntityAsync<TEntity, TOrderBy, TResult>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TOrderBy>> orderby, Expression<Func<TEntity, TResult>> selector, bool IsAsc) 130 where TEntity : class 131 where TResult : class; 132 133 /// <summary> 134 /// 可指定返回结果、排序、查询条件的通用查询方法,返回Object对象集合 135 /// </summary> 136 /// <typeparam name="TEntity">实体对象</typeparam> 137 /// <typeparam name="TOrderBy">排序字段类型</typeparam> 138 /// <param name="where">过滤条件,需要用到类型转换的需要提前处理与数据表一致的</param> 139 /// <param name="orderby">排序字段</param> 140 /// <param name="selector">返回结果(必须是模型中存在的字段)</param> 141 /// <param name="IsAsc">排序方向,true为正序false为倒序</param> 142 /// <returns>自定义实体集合</returns> 143 List<object> QueryObject<TEntity, TOrderBy>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TOrderBy>> orderby, Func<IQueryable<TEntity>, List<object>> selector, bool IsAsc) 144 where TEntity : class; 145 /// <summary> 146 /// 可指定返回结果、排序、查询条件的通用查询方法,返回Object对象集合(异步方式) 147 /// </summary> 148 /// <typeparam name="TEntity">实体对象</typeparam> 149 /// <typeparam name="TOrderBy">排序字段类型</typeparam> 150 /// <param name="where">过滤条件,需要用到类型转换的需要提前处理与数据表一致的</param> 151 /// <param name="orderby">排序字段</param> 152 /// <param name="selector">返回结果(必须是模型中存在的字段)</param> 153 /// <param name="IsAsc">排序方向,true为正序false为倒序</param> 154 /// <returns>自定义实体集合</returns> 155 Task<List<object>> QueryObjectAsync<TEntity, TOrderBy>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TOrderBy>> orderby, Func<IQueryable<TEntity>, List<object>> selector, bool IsAsc) 156 where TEntity : class; 157 158 /// <summary> 159 /// 可指定返回结果、排序、查询条件的通用查询方法,返回动态类对象集合 160 /// </summary> 161 /// <typeparam name="TEntity">实体对象</typeparam> 162 /// <typeparam name="TOrderBy">排序字段类型</typeparam> 163 /// <param name="where">过滤条件,需要用到类型转换的需要提前处理与数据表一致的</param> 164 /// <param name="orderby">排序字段</param> 165 /// <param name="selector">返回结果(必须是模型中存在的字段)</param> 166 /// <param name="IsAsc">排序方向,true为正序false为倒序</param> 167 /// <returns>动态类</returns> 168 dynamic QueryDynamic<TEntity, TOrderBy>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TOrderBy>> orderby, Func<IQueryable<TEntity>, List<object>> selector, bool IsAsc) 169 where TEntity : class; 170 /// <summary> 171 /// 可指定返回结果、排序、查询条件的通用查询方法,返回动态类对象集合(异步方式) 172 /// </summary> 173 /// <typeparam name="TEntity">实体对象</typeparam> 174 /// <typeparam name="TOrderBy">排序字段类型</typeparam> 175 /// <param name="where">过滤条件,需要用到类型转换的需要提前处理与数据表一致的</param> 176 /// <param name="orderby">排序字段</param> 177 /// <param name="selector">返回结果(必须是模型中存在的字段)</param> 178 /// <param name="IsAsc">排序方向,true为正序false为倒序</param> 179 /// <returns>动态类</returns> 180 Task<dynamic> QueryDynamicAsync<TEntity, TOrderBy>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TOrderBy>> orderby, Func<IQueryable<TEntity>, List<object>> selector, bool IsAsc) 181 where TEntity : class; 182 183 #endregion
修改验证是否存在,同时增加异步方式:
1 #region 验证是否存在 2 3 /// <summary> 4 /// 验证当前条件是否存在相同项 5 /// </summary> 6 bool IsExist(Expression<Func<T, bool>> predicate); 7 /// <summary> 8 /// 验证当前条件是否存在相同项(异步方式) 9 /// </summary> 10 Task<bool> IsExistAsync(Expression<Func<T, bool>> predicate); 11 12 /// <summary> 13 /// 根据SQL验证实体对象是否存在 14 /// </summary> 15 bool IsExist(string sql, params DbParameter[] para); 16 /// <summary> 17 /// 根据SQL验证实体对象是否存在(异步方式) 18 /// </summary> 19 Task<bool> IsExistAsync(string sql, params DbParameter[] para); 20 21 #endregion
其余,分页查询、ADO.NET操作、更新字段 这几个方法,我们暂时不做修改:
1 #region 分页查询 2 /// <summary> 3 /// 通用EF分页,默认显示20条记录 4 /// </summary> 5 /// <typeparam name="TEntity">实体模型</typeparam> 6 /// <typeparam name="TOrderBy">排序类型</typeparam> 7 /// <param name="index">当前页</param> 8 /// <param name="pageSize">显示条数</param> 9 /// <param name="where">过滤条件</param> 10 /// <param name="orderby">排序字段</param> 11 /// <param name="selector">结果集合</param> 12 /// <param name="isAsc">排序方向true正序 false倒序</param> 13 /// <returns>自定义实体集合</returns> 14 PageInfo<object> Query<TEntity, TOrderBy> 15 (int index, int pageSize, 16 Expression<Func<TEntity, bool>> where, 17 Expression<Func<TEntity, TOrderBy>> orderby, 18 Func<IQueryable<TEntity>, List<object>> selector, 19 bool IsAsc) 20 where TEntity : class; 21 /// <summary> 22 /// 对IQueryable对象进行分页逻辑处理,过滤、查询项、排序对IQueryable操作 23 /// </summary> 24 /// <param name="t">Iqueryable</param> 25 /// <param name="index">当前页</param> 26 /// <param name="PageSize">每页显示多少条</param> 27 /// <returns>当前IQueryable to List的对象</returns> 28 Common.PageInfo<T> Query(IQueryable<T> query, int index, int PageSize); 29 /// <summary> 30 /// 普通SQL查询分页方法 31 /// </summary> 32 /// <param name="index">当前页</param> 33 /// <param name="pageSize">显示行数</param> 34 /// <param name="tableName">表名/视图</param> 35 /// <param name="field">获取项</param> 36 /// <param name="filter">过滤条件</param> 37 /// <param name="orderby">排序字段+排序方向</param> 38 /// <param name="group">分组字段</param> 39 /// <returns>结果集</returns> 40 Common.PageInfo Query(int index, int pageSize, string tableName, string field, string filter, string orderby, string group, params DbParameter[] para); 41 /// <summary> 42 /// 简单的Sql查询分页 43 /// </summary> 44 /// <param name="index"></param> 45 /// <param name="pageSize"></param> 46 /// <param name="sql"></param> 47 /// <returns></returns> 48 Common.PageInfo Query(int index, int pageSize, string sql,string orderby,params DbParameter[] para); 49 /// <summary> 50 /// 多表联合分页算法 51 /// </summary> 52 PageInfo Query(IQueryable query, int index, int pagesize); 53 #endregion 54 55 #region ADO.NET增删改查方法 56 /// <summary> 57 /// 执行增删改方法,含事务处理 58 /// </summary> 59 object ExecuteSqlCommand(string sql, params DbParameter[] para); 60 /// <summary> 61 /// 执行多条SQL,增删改方法,含事务处理 62 /// </summary> 63 object ExecuteSqlCommand(Dictionary<string, object> sqllist); 64 /// <summary> 65 /// 执行查询方法,返回动态类,接收使用var,遍历时使用dynamic类型 66 /// </summary> 67 object ExecuteSqlQuery(string sql, params DbParameter[] para); 68 #endregion 69 70 #region 更新操作 71 /// <summary> 72 /// 更新字段 73 /// </summary> 74 /// <param name="table">表名</param> 75 /// <param name="dic">被解析的字段</param> 76 /// <param name="where">条件</param> 77 /// <returns></returns> 78 bool Modify(string table, Dictionary<string, object> dic, string where); 79 #endregion
修改前的RepositoryBase:链接:http://pan.baidu.com/s/1pKYRIkF 密码:uj2u
修改后的代码,大家先不要直接替换掉项目中的RepositoryBase,我们的接口和实现类也需要做一些修改。我们在下一章,修改接口和实现类,改完后,可以直接覆盖掉。
原创文章 转载请尊重劳动成果 http://yuangang.cnblogs.com