本篇我将带着大家一起来对Dapper进行下封装并实现基本的增删改查、分页操作的同步异步方法的实现(已实现MSSQL,MySql,PgSQL)。同时我们再实现一下仓储层的代码生成器,这样的话,我们只需要结合业务来实现具体的业务部分的代码就可以了,可以大大减少我们重复而又繁琐的增删改查操作,多留点时间给生活充充电(不会偷懒的程序员不是一位好爸爸/好老公/好男朋友)。如果您觉得我的实现过程有所不妥的话,您可以在评论区留言,或者加入我们的千人.Net Core实战项目交流群637326624交流。另外如果您觉得我的文章对您有所帮助的话希望给个推荐以示支持。项目的源代码我会托管在GayHub上,地址在文章末尾会给出,自认为代码写的很工整,注释也很全,你应该能看懂!

本文已收录至《.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划》

作者:依乐祝

原文地址:https://www.cnblogs.com/yilezhu/p/10146311.html

写在前面

将近一周没有更新,鬼知道我这么长时间在干什么,你可以认为我在憋大招,在想着怎么给大家分享更多更实用的东西。其实这只是我偷懒的借口罢了!下面我们一起来对Dapper进行下封装吧,然后结合Dapper.SimpleCRUD 来实现基本的增删改查、分页操作。这部分功能实现完成后,往下我们也就是基于这些基本操作来实现我们的CMS的业务了,如:权限部分,菜单部分,文章部分的功能。接下来我会对这部分快速的实现,可能会很少更新了,因为这些都是基本的CMS的业务操作,没多少要分享的内容,毕竟每个人的系统业务都不一样,这部分的业务实现也是千差万别的。我后期会把成品直接分享给大家!敬请关注吧!

Dapper的封装

IDbConnection工厂类的封装

这部分我实现了一个IDbConnection的工厂类,以便你可以很方便的根据数据库的类型来创建不同的IDbConnection对象,目前已实现对SqlServer,MySQL,PostgreSQL的实现,具体代码如下,根据传入的参数来进行相关的实现。

       /// <summary>/// yilezhu/// 2018.12.13/// 数据库连接工厂类/// </summary>public class ConnectionFactory{/// <summary>/// 获取数据库连接/// </summary>/// <param name="dbtype">数据库类型</param>/// <param name="conStr">数据库连接字符串</param>/// <returns>数据库连接</returns>public static IDbConnection CreateConnection(string dbtype, string strConn){if (dbtype.IsNullOrWhiteSpace())throw new ArgumentNullException("获取数据库连接居然不传数据库类型,你想上天吗?");if (strConn.IsNullOrWhiteSpace())throw new ArgumentNullException("获取数据库连接居然不传数据库类型,你想上天吗?");var dbType = GetDataBaseType(dbtype);return CreateConnection(dbType,strConn);}/// <summary>/// 获取数据库连接/// </summary>/// <param name="dbType">数据库类型</param>/// <param name="conStr">数据库连接字符串</param>/// <returns>数据库连接</returns>public static IDbConnection CreateConnection(DatabaseType dbType, string strConn){IDbConnection connection = null;           if (strConn.IsNullOrWhiteSpace())throw new ArgumentNullException("获取数据库连接居然不传数据库类型,你想上天吗?");switch (dbType){case DatabaseType.SqlServer:connection = new SqlConnection(strConn);break;case DatabaseType.MySQL:connection = new MySqlConnection(strConn);break;case DatabaseType.PostgreSQL:connection = new NpgsqlConnection(strConn);break;default:throw new ArgumentNullException($"这是我的错,还不支持的{dbType.ToString()}数据库类型");}if (connection.State == ConnectionState.Closed){connection.Open();}return connection;}/// <summary>/// 转换数据库类型/// </summary>/// <param name="dbtype">数据库类型字符串</param>/// <returns>数据库类型</returns>public static DatabaseType GetDataBaseType(string dbtype){if (dbtype.IsNullOrWhiteSpace())throw new ArgumentNullException("获取数据库连接居然不传数据库类型,你想上天吗?");DatabaseType returnValue = DatabaseType.SqlServer;foreach (DatabaseType dbType in Enum.GetValues(typeof(DatabaseType))){if (dbType.ToString().Equals(dbtype, StringComparison.OrdinalIgnoreCase)){returnValue = dbType;break;}}return returnValue;}}

那么,我们怎么来使用这个工厂类呢?如下给出调用的实例。

是不是很简单,感觉瞬间少了很多代码,这段代码摘录自代码生成器里面。有兴趣的自己去查看源码吧!

CRUD及分页泛型方法的实现

  1. nuget安装Dapper.SimpleCRUD ,什么你要问我怎么安装?乖乖的回去看第二篇文章吧!那里会教你如何安装Nuget包?如果那篇文章里面没有,那你就好好想想为啥没有呢?

  1. 新建IBaseRepository泛型接口 定义如下的增删改查方法的同步异步接口,其中还包含分页的实现,具体的代码如下:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描    述:
    *│ 作    者:yilezhu
    *│ 版    本:1.0
    *│ 创建时间:2018/12/16 20:41:22
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空间: Czar.Cms.Core.Repository
    *│ 接口名称: IBaseRepository
    *└──────────────────────────────────────────────────────────────┘
    */
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;namespace Czar.Cms.Core.Repository
    {public interface IBaseRepository<T,TKey> :  IDisposable where T : class{#region 同步/// <summary>/// 通过主键获取实体对象/// </summary>/// <param name="id">主键ID</param>/// <returns></returns>T Get(TKey id);/// <summary>/// 获取所有的数据/// </summary>/// <returns></returns>IEnumerable<T> GetList();/// <summary>/// 执行具有条件的查询,并将结果映射到强类型列表/// </summary>/// <param name="whereConditions">条件</param>/// <returns></returns>IEnumerable<T> GetList(object whereConditions);/// <summary>/// 带参数的查询满足条件的数据/// </summary>/// <param name="conditions">条件</param>/// <param name="parameters">参数</param>/// <returns></returns>IEnumerable<T> GetList(string conditions, object parameters = null);/// <summary>/// 使用where子句执行查询,并将结果映射到具有Paging的强类型List/// </summary>/// <param name="pageNumber">页码</param>/// <param name="rowsPerPage">每页显示数据</param>/// <param name="conditions">查询条件</param>/// <param name="orderby">排序</param>/// <param name="parameters">参数</param>/// <returns></returns>IEnumerable<T> GetListPaged(int pageNumber, int rowsPerPage, string conditions, string orderby, object parameters = null);/// <summary>/// 插入一条记录并返回主键值(自增类型返回主键值,否则返回null)/// </summary>/// <param name="entity"></param>/// <returns></returns>int? Insert(T entity);/// <summary>/// 更新一条数据并返回影响的行数/// </summary>/// <param name="entity"></param>/// <returns>影响的行数</returns>int Update(T entity);/// <summary>/// 根据实体主键删除一条数据/// </summary>/// <param name="id">主键</param>/// <returns>影响的行数</returns>int Delete(TKey id);/// <summary>/// 根据实体删除一条数据/// </summary>/// <param name="entity">实体</param>/// <returns>返回影响的行数</returns>int Delete(T entity);/// <summary>/// 条件删除多条记录/// </summary>/// <param name="whereConditions">条件</param>/// <param name="transaction">事务</param>/// <param name="commandTimeout">超时时间</param>/// <returns>影响的行数</returns>int DeleteList(object whereConditions, IDbTransaction transaction = null, int? commandTimeout = null);/// <summary>/// 使用where子句删除多个记录/// </summary>/// <param name="conditions">wher子句</param>/// <param name="parameters">参数</param>/// <param name="transaction">事务</param>/// <param name="commandTimeout">超时时间</param>/// <returns>影响的行数</returns>int DeleteList(string conditions, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null);/// <summary>/// 满足条件的记录数量/// </summary>/// <param name="conditions"></param>/// <param name="parameters"></param>/// <returns></returns>int RecordCount(string conditions = "", object parameters = null);#endregion#region 异步/// <summary>/// 通过主键获取实体对象/// </summary>/// <param name="id">主键ID</param>/// <returns></returns>Task<T> GetAsync(TKey id);/// <summary>/// 获取所有的数据/// </summary>/// <returns></returns>Task<IEnumerable<T>> GetListAsync();/// <summary>/// 执行具有条件的查询,并将结果映射到强类型列表/// </summary>/// <param name="whereConditions">条件</param>/// <returns></returns>Task<IEnumerable<T>> GetListAsync(object whereConditions);/// <summary>/// 带参数的查询满足条件的数据/// </summary>/// <param name="conditions">条件</param>/// <param name="parameters">参数</param>/// <returns></returns>Task<IEnumerable<T>> GetListAsync(string conditions, object parameters = null);/// <summary>/// 使用where子句执行查询,并将结果映射到具有Paging的强类型List/// </summary>/// <param name="pageNumber">页码</param>/// <param name="rowsPerPage">每页显示数据</param>/// <param name="conditions">查询条件</param>/// <param name="orderby">排序</param>/// <param name="parameters">参数</param>/// <returns></returns>Task<IEnumerable<T>> GetListPagedAsync(int pageNumber, int rowsPerPage, string conditions, string orderby, object parameters = null);/// <summary>/// 插入一条记录并返回主键值/// </summary>/// <param name="entity"></param>/// <returns></returns>Task<int?> InsertAsync(T entity);/// <summary>/// 更新一条数据并返回影响的行数/// </summary>/// <param name="entity"></param>/// <returns>影响的行数</returns>Task<int> UpdateAsync(T entity);/// <summary>/// 根据实体主键删除一条数据/// </summary>/// <param name="id">主键</param>/// <returns>影响的行数</returns>Task<int> DeleteAsync(TKey id);/// <summary>/// 根据实体删除一条数据/// </summary>/// <param name="entity">实体</param>/// <returns>返回影响的行数</returns>Task<int> DeleteAsync(T entity);/// <summary>/// 条件删除多条记录/// </summary>/// <param name="whereConditions">条件</param>/// <param name="transaction">事务</param>/// <param name="commandTimeout">超时时间</param>/// <returns>影响的行数</returns>Task<int> DeleteListAsync(object whereConditions, IDbTransaction transaction = null, int? commandTimeout = null);/// <summary>/// 使用where子句删除多个记录/// </summary>/// <param name="conditions">wher子句</param>/// <param name="parameters">参数</param>/// <param name="transaction">事务</param>/// <param name="commandTimeout">超时时间</param>/// <returns>影响的行数</returns>Task<int> DeleteListAsync(string conditions, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null);/// <summary>/// 满足条件的记录数量/// </summary>/// <param name="conditions"></param>/// <param name="parameters"></param>/// <returns></returns>Task<int> RecordCountAsync(string conditions = "", object parameters = null);#endregion}
    }
    
  2. 然后创建一个BaseRepository泛型类来实现上面的接口,其中多了两个成员,DbOpion以及IDbConnection,猜猜看这两个东西有什么用?后面给出答案

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描    述:仓储类的基类
    *│ 作    者:yilezhu
    *│ 版    本:1.0
    *│ 创建时间:2018/12/16 12:03:02
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空间: Czar.Cms.Core.Repository
    *│ 类    名: BaseRepository
    *└──────────────────────────────────────────────────────────────┘
    */
    using Czar.Cms.Core.DbHelper;
    using Czar.Cms.Core.Options;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;
    using Dapper;namespace Czar.Cms.Core.Repository
    {public class BaseRepository<T, TKey> : IBaseRepository<T, TKey> where T : class{protected DbOpion _dbOpion;protected IDbConnection _dbConnection;//public BaseRepository(DbOpion dbOpion)//{//    _dbOpion = dbOpion ?? throw new ArgumentNullException(nameof(DbOpion));//    _dbConnection = ConnectionFactory.CreateConnection(_dbOpion.DbType, _dbOpion.ConnectionString);//}#region 同步public T Get(TKey id) => _dbConnection.Get<T>(id);public IEnumerable<T> GetList() => _dbConnection.GetList<T>();public IEnumerable<T> GetList(object whereConditions) => _dbConnection.GetList<T>(whereConditions);public IEnumerable<T> GetList(string conditions, object parameters = null) => _dbConnection.GetList<T>(conditions, parameters);public IEnumerable<T> GetListPaged(int pageNumber, int rowsPerPage, string conditions, string orderby, object parameters = null){return _dbConnection.GetListPaged<T>(pageNumber, rowsPerPage, conditions, orderby, parameters);}public int? Insert(T entity) => _dbConnection.Insert(entity);public int Update(T entity) => _dbConnection.Update(entity);public int Delete(TKey id) => _dbConnection.Delete<T>(id);public int Delete(T entity) => _dbConnection.Delete(entity);public int DeleteList(object whereConditions, IDbTransaction transaction = null, int? commandTimeout = null){return _dbConnection.DeleteList<T>(whereConditions, transaction, commandTimeout);}public int DeleteList(string conditions, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null){return _dbConnection.DeleteList<T>(conditions, parameters, transaction, commandTimeout);}public int RecordCount(string conditions = "", object parameters = null){return _dbConnection.RecordCount<T>(conditions, parameters);}#endregion#region 异步public async Task<T> GetAsync(TKey id){return await _dbConnection.GetAsync<T>(id);}public async Task<IEnumerable<T>> GetListAsync(){return await _dbConnection.GetListAsync<T>();}public async Task<IEnumerable<T>> GetListAsync(object whereConditions){return await _dbConnection.GetListAsync<T>(whereConditions);}public async Task<IEnumerable<T>> GetListAsync(string conditions, object parameters = null){return await _dbConnection.GetListAsync<T>(conditions, parameters);}public async Task<IEnumerable<T>> GetListPagedAsync(int pageNumber, int rowsPerPage, string conditions, string orderby, object parameters = null){return await _dbConnection.GetListPagedAsync<T>(pageNumber, rowsPerPage, conditions, orderby, parameters);}public async Task<int?> InsertAsync(T entity){return await _dbConnection.InsertAsync(entity);}public async Task<int> UpdateAsync(T entity){return await _dbConnection.UpdateAsync(entity);}public async Task<int> DeleteAsync(TKey id){return await _dbConnection.DeleteAsync(id);}public async Task<int> DeleteAsync(T entity){return await _dbConnection.DeleteAsync(entity);}public async Task<int> DeleteListAsync(object whereConditions, IDbTransaction transaction = null, int? commandTimeout = null){return await _dbConnection.DeleteListAsync<T>(whereConditions, transaction, commandTimeout);}public async Task<int> DeleteListAsync(string conditions, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null){return await DeleteListAsync(conditions, parameters, transaction, commandTimeout);}public async Task<int> RecordCountAsync(string conditions = "", object parameters = null){return await _dbConnection.RecordCountAsync<T>(conditions, parameters);}#endregion#region IDisposable Supportprivate bool disposedValue = false; // 要检测冗余调用protected virtual void Dispose(bool disposing){if (!disposedValue){if (disposing){// TODO: 释放托管状态(托管对象)。}// TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。// TODO: 将大型字段设置为 null。disposedValue = true;}}// TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。// ~BaseRepository() {//   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。//   Dispose(false);// }// 添加此代码以正确实现可处置模式。public void Dispose(){// 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。Dispose(true);// TODO: 如果在以上内容中替代了终结器,则取消注释以下行。// GC.SuppressFinalize(this);}#endregion}
    }
    

你没看错?我在16号就已经写好了,为什么这么晚才写博客分享出来呢?因为我懒~~~

  1. 这里需要注意,需要安装SimpleCRUD的Nuget包。另外其他的仓储方法只需要继承这个接口以及实现就能够实现基本的增删改查操作了。这里你应该会想,既然继承就能实现,那何不写一个仓储的代码生成器来进行生成呢?说干就干,下面我们就来实现仓储的代码生成器

仓储层代码生成器

上篇生成数据库实体的代码生成器不知道大家看了没有,这里我们只需要在根据每个数据库表生成数据库实体的实体顺带着生成下仓储接口以及仓储代码就可以了。有了思路,我们就撸起袖子加油干吧

  1. 先写一下仓储接口代码生成器的模板,如下所示:

  1. 再写一下仓储层的代码实现,这里需要注意一下,需要根据注入的IOptionsSnapshot来生成_dbOpion以及_dbConnection,上面留给大家的思考题答案就在这里,如下所示:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描    述:{Comment}接口实现
    *│ 作    者:{Author}
    *│ 版    本:1.0    模板代码自动生成
    *│ 创建时间:{GeneratorTime}
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空间: {RepositoryNamespace}
    *│ 类    名: {ModelName}Repository
    *└──────────────────────────────────────────────────────────────┘
    */
    using Czar.Cms.Core.DbHelper;
    using Czar.Cms.Core.Options;
    using Czar.Cms.Core.Repository;
    using Czar.Cms.IRepository;
    using Czar.Cms.Models;
    using Microsoft.Extensions.Options;
    using System;namespace {RepositoryNamespace}
    {public class {ModelName}Repository:BaseRepository<{ModelName},{KeyTypeName}>, I{ModelName}Repository{public {ModelName}Repository(IOptionsSnapshot<DbOpion> options){_dbOpion =options.Get("CzarCms");if (_dbOpion == null){throw new ArgumentNullException(nameof(DbOpion));}_dbConnection = ConnectionFactory.CreateConnection(_dbOpion.DbType, _dbOpion.ConnectionString);}}
    }
  2. 接着就是代码生成器生成IRepository以及生成Repository的代码了!这部分代码如下图所示:

测试代码

  1. 重新执行下代码生成器的代码,测试的具体代码我已经放在GitHub上了,这里就不贴出来了,直接上生成结果如下图所示:

  1. 如上图所示:一次性生成了Models以及Repository,IRepository的代码,然后到每个文件夹里面把对应的代码拷贝到对应的项目里面吧。然后我们随便打开一下仓储以及仓储接口看下生成后的代码如下所示:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描    述:文章分类
    *│ 作    者:yilezhu
    *│ 版    本:1.0   模板代码自动生成
    *│ 创建时间:2018-12-18 13:28:43
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空间: Czar.Cms.IRepository
    *│ 接口名称: IArticleCategoryRepository
    *└──────────────────────────────────────────────────────────────┘
    */
    using Czar.Cms.Core.Repository;
    using Czar.Cms.Models;
    using System;namespace Czar.Cms.IRepository
    {public interface IArticleCategoryRepository : IBaseRepository<ArticleCategory, Int32>{}
    }
/**
*┌──────────────────────────────────────────────────────────────┐
*│ 描    述:文章分类接口实现
*│ 作    者:yilezhu
*│ 版    本:1.0    模板代码自动生成
*│ 创建时间:2018-12-18 13:28:43
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│ 命名空间: Czar.Cms.Repository.SqlServer
*│ 类    名: ArticleCategoryRepository
*└──────────────────────────────────────────────────────────────┘
*/
using Czar.Cms.Core.DbHelper;
using Czar.Cms.Core.Options;
using Czar.Cms.Core.Repository;
using Czar.Cms.IRepository;
using Czar.Cms.Models;
using Microsoft.Extensions.Options;
using System;namespace Czar.Cms.Repository.SqlServer
{public class ArticleCategoryRepository:BaseRepository<ArticleCategory,Int32>, IArticleCategoryRepository{public ArticleCategoryRepository(IOptionsSnapshot<DbOpion> options){_dbOpion =options.Get("CzarCms");if (_dbOpion == null){throw new ArgumentNullException(nameof(DbOpion));}_dbConnection = ConnectionFactory.CreateConnection(_dbOpion.DbType, _dbOpion.ConnectionString);}}
}
  1. 在仓储层以及仓储接口层添加对Czar.Cms.Core的引用,当然你也可以通过Nuget包来进行安装

    Install-Package Czar.Cms.Core -Version 0.1.3
  2. 最后在测试代码中进行测试,这里以ArticleCategoryRepository为例进行测试:

    [Fact]
    public void TestBaseFactory()
    {IServiceProvider serviceProvider = BuildServiceForSqlServer();IArticleCategoryRepository categoryRepository = serviceProvider.GetService<IArticleCategoryRepository>();var category = new ArticleCategory{Title = "随笔",ParentId = 0,ClassList = "",ClassLayer = 0,Sort = 0,ImageUrl = "",SeoTitle = "随笔的SEOTitle",SeoKeywords = "随笔的SeoKeywords",SeoDescription = "随笔的SeoDescription",IsDeleted = false,};var categoryId = categoryRepository.Insert(category);var list = categoryRepository.GetList();Assert.True(1 == list.Count());Assert.Equal("随笔", list.FirstOrDefault().Title);Assert.Equal("SQLServer", DatabaseType.SqlServer.ToString(), ignoreCase: true);categoryRepository.Delete(categoryId.Value);var count = categoryRepository.RecordCount();Assert.True(0 == count);
  3. 测试结果如下所示,都已经测试成功了:

开原地址

这个系列教程的源码我会开放在GitHub以及码云上,有兴趣的朋友可以下载查看!觉得不错的欢迎Star

GitHub:https://github.com/yilezhu/Czar.Cms

码云:https://gitee.com/yilezhu/Czar.Cms

如果你觉得这个系列对您有所帮助的话,欢迎以各种方式进行赞助,当然给个Star支持下也是可以滴!另外一种最简单粗暴的方式就是下面这种直接关注我们的公众号了
第一时间收到更新推送。

总结

一路走来,已经更新到第十二篇了,到这里大伙已经可以基于这个Dapper的封装进行自己的业务系统的开发了!当然接下来我会继续完成我们既定的CMS系统的业务功能开发,接下来可以用来分享的东西就很少了,所以我更多的是开发然后把代码更新到GitHub以及码云上,想看最新的代码就获取dev分支的代码,有问题的可以提issue或者群里讨论!敬请期待吧!

.NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现...相关推荐

  1. .NET Core实战项目之CMS 第十四章 开发篇-防止跨站请求伪造(XSRF/CSRF)攻击处理...

    通过 ASP.NET Core,开发者可轻松配置和管理其应用的安全性. ASP.NET Core 中包含管理身份验证.授权.数据保护.SSL 强制.应用机密.请求防伪保护及 CORS 管理等等安全方面 ...

  2. .NET Core实战项目之CMS 第十六章 用户登录及验证码功能实现

    前面为了方便我们只是简单实现了基本业务功能的增删改查,但是登录功能还没有实现,而登录又是系统所必须的,得益于 ASP.NET Core的可扩展性因此我们很容易实现我们的登录功能.今天我将带着大家一起来 ...

  3. .NET Core实战项目之CMS 第十五章 各层联动工作实现增删改查业务

    连着两天更新叙述性的文章大家可别以为我转行了!哈哈!今天就继续讲讲我们的.NET Core实战项目之CMS系统的教程吧!这个系列教程拖得太久了,所以今天我就以菜单部分的增删改查为例来讲述下我的项目分层 ...

  4. .NET Core实战项目之CMS 第十七章 CMS网站系统的部署

    目前我们的.NET Core实战项目之CMS系列教程基本走到尾声了,通过这一系列的学习你应该能够轻松应对.NET Core的日常开发了!当然这个CMS系统的一些逻辑处理还需要优化,如没有引入日志组件以 ...

  5. .NET Core实战项目之CMS 第十三章 开发篇-在MVC项目结构介绍及应用第三方UI

    作为后端开发的我来说,前端表示真心玩不转,你如果让我微调一个位置的样式的话还行,但是让我写一个很漂亮的后台的话,真心做不到,所以我一般会选择套用一些开源UI模板来进行系统UI的设计.那如何套用呢?今天 ...

  6. .NET Core实战项目之CMS 第十一章 开发篇-数据库生成及实体代码生成器开发

    上篇给大家从零开始搭建了一个我们的ASP.NET Core CMS系统的开发框架,具体为什么那样设计我也已经在第十篇文章中进行了说明.不过文章发布后很多人都说了这样的分层不是很合理,什么数据库实体应该 ...

  7. .NET Core实战项目之CMS 第九章 设计篇-白话架构设计

    前面两篇文章给大家介绍了我们实战的CMS系统的数据库设计,源码也已经上传到服务器上了.今天我们就好聊聊架构设计,在开始之前先给大家分享一下这几天我一直在听的<从零开始学架构>里面关于架构设 ...

  8. .NET Core实战项目之CMS 第四章 入门篇-Git的快速入门及实战演练

    写在前面 上篇文章.NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入我带着大家通过分析了一遍ASP.NET Core的源码了解了它的启动过程,然后又带着大家熟悉了一遍配置文 ...

  9. .NET Core实战项目之CMS 第十章 设计篇-系统开发框架设计

    这两天比较忙,周末也在加班,所以更新的就慢了一点,不过没关系,今天我们就进行千呼万唤的系统开发框架的设计.不知道上篇关于架构设计的文章大家有没有阅读,如果阅读后相信一定对架构设计有了更近一部的理解,如 ...

最新文章

  1. 什么是ThreadLocal
  2. 马尔可夫模型与条件随机场模型
  3. Android滚动页面位置指示器:CircleIndicator
  4. 用Tableau画3D模型之四(放弃篇)
  5. oracle 复制组删除,利用copy在ASM磁盘组之间迁移
  6. 学习NodeJS第一天:node.js引言
  7. 爬虫 spider09——爬取指定数据,去重复,并存储到mysql
  8. php对html加密解密,PHP Mcrypt和HTML5加密API加密/解密
  9. python探测端口_Python实现端口检测
  10. SQL中返回刚插入记录的ID
  11. 计算机毕业设计:基于springboot框架开发的办公自动化OA系统
  12. UI设计,扁平化还是拟物化?
  13. 世界各国及其省份城市经纬度
  14. 一款红队大量资产指纹探测工具
  15. 【论文阅读|浅读】DeepEmLAN: Deep embedding learning for attributed networks
  16. 还在为微信朋友圈的大量广告而苦恼吗?一文教你如何清除微信朋友圈的广告!!!
  17. java 崩溃监控,求大神分析drwtsn32 监控javaee程序崩溃日记
  18. Win32 API 函数列表1(格式有点乱)
  19. 电信中兴光猫ZXHN F650超管密码获取工具
  20. linux磁盘空间不释放问题排查

热门文章

  1. 上市 10 天就遭破解!AirTag 还能买吗?
  2. 赔偿 525 万?联想前副总裁跳槽小米仲裁案后续,常程不服提起诉讼
  3. 搜索关键词分析——以个人博客网站为例
  4. Apache Pulsar中的地域复制,第1篇:概念和功能
  5. select sum也会返回null值
  6. 认识网页:html + css + JavaScript
  7. 小白该如何学习Linux操作系统(2)
  8. 【转载】通过sqlserver日志恢复误删除的数据
  9. Meteor:网络应用开发的瑞士军刀
  10. Eclipse启动之四 : Eclipse核心框架启动(百度空间迁移)