42歲大齡程式設計師的迷茫,看我最新嘗鮮.net 5+Dapper搭建的WebAPI框架

42歲大齡程式設計師的迷茫

我真傻,真的。我單知道雪天是野獸在深山裡沒有食吃,會到村裡來;我不知道春天也會有……

我真傻,真的。我單知道程式設計師要活到老學到老,年齡大了要失業;我不知道碼農(新型農民工)也會有……

上周回老家有點無聊就去小破站看了點影片,是講Dapr的實踐(朝夕教育某講師的公開課錄屏),看完非常之震撼:原來微服務離我那麼近!

雖然有失業的風險,但是我還是覺得技術人嘛,養家糊口應該沒問題的,壓力是有點大,但是「辦法總比困難多」。所以其實我也不迷茫……

好長時間沒有更新部落格了,因為我覺得「Show me the Code!」比較重要,最近用.net 5+Dapper搭建了一個WebAPI的開發框架,今天分享給大夥。

幾年前有一篇類似的文章大夥可以回顧一下:一次asp.net core3.1打造webapi開發框架的實踐

開始show you the code

實踐技術看點

  • 1、Swagger管理API說明文檔
  • 2、JwtBearer token驗證
  • 3、Swagger UI增加Authentication
  • 4、Dapper實現的Repository
  • 5、在.net 5下使用Log4net
  • 6、與釘釘開放平台交互

項目中使用到的包清單

 

 <ItemGroup>
    <PackageReference Include="Hangfire.AspNetCore" Version="1.7.24" />
    <PackageReference Include="Hangfire.HttpJob.Agent" Version="1.4.2" />
    <PackageReference Include="Hangfire.HttpJob.Agent.MssqlConsole" Version="1.4.2" />
    <PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
    <PackageReference Include="Hangfire.SqlServer" Version="1.7.24" />
    <PackageReference Include="log4net" Version="2.0.12" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.9" />
    <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.9" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
    <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.1.5" />
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.1.5" />
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.1.5" />
  </ItemGroup>

 

 

關鍵程式碼展示:

1)StartUP

這個只貼圖吧,教程大夥都看吐了,司空見慣:

 

 用的是標準的套路熟悉的程式碼。

     #region JWT
            services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
            var token = Configuration.GetSection("tokenManagement").Get<TokenManagement>();
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
                    ValidIssuer = token.Issuer,
                    ValidAudience = token.Audience,
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });
            #endregion
 #region Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1",
                    new OpenApiInfo
                    {
                        Title = "TSP車載MES介面文檔",
                        Version = "v1",
                        Contact = new OpenApiContact
                        {
                            Email = "[email protected]",
                            Name = "MES團隊",
                            Url = new Uri("//www.ts-precision.com/")
                        }
                    });
                // 為 Swagger 設置xml文檔注釋路徑
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);
                c.AddSecurityDefinition("Bearer",
                    new OpenApiSecurityScheme
                    {
                        Description = "請輸入OAuth介面返回的Token,前置Bearer。示例:Bearer {Roken}",
                        Name = "Authorization",
                        In = ParameterLocation.Header,
                        Type = SecuritySchemeType.ApiKey
                    });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                   {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference()
                            {
                                Id = "Bearer",
                                Type = ReferenceType.SecurityScheme
                            }
                        }, Array.Empty<string>()
                    }
                });
            });
            #endregion
 #region Hangfire
            string HangfireConn = Configuration.GetConnectionString("HangfireDB");
            //SqlServer持久性
            services.AddHangfire(x => x.UseStorage(new SqlServerStorage(
                                                      HangfireConn,
                                                      new SqlServerStorageOptions
                                                      { 
                                                            QueuePollInterval = TimeSpan.FromSeconds(15),//- 作業隊列輪詢間隔。默認值為15秒。
                                                            JobExpirationCheckInterval = TimeSpan.FromHours(1),//- 作業到期檢查間隔(管理過期記錄)。默認值為1小時。
                                                            CountersAggregateInterval = TimeSpan.FromMinutes(5),//- 聚合計數器的間隔。默認為5分鐘。
                                                            PrepareSchemaIfNecessary = true,//- 如果設置為true,則創建資料庫表。默認是true。
                                                            DashboardJobListLimit = 50000,//- 儀錶板作業列表限制。默認值為50000。
                                                            TransactionTimeout = TimeSpan.FromMinutes(1),//- 交易超時。默認為1分鐘。
                                                       }))
                                );
            services.AddHangfireHttpJobAgent();
            //Hangfire非持久性
            //services.AddHangfire(x => x.UseStorage(new MemoryStorage())); 
            #endregion

 2)Dapper相關:

DynamicQuery實現半自動sql編寫:

 public static class DynamicQuery
    {
        public static string GetUpdateQuery(string tableName, dynamic item)
        {
            PropertyInfo[] props = item.GetType().GetProperties();
            string[] columns = props.Select(p => p.Name).ToArray();
            List<string> parameters = columns.Select(name => name + "=@" + name).ToList();
            return string.Format("UPDATE {0} SET {1} WHERE ID=@ID", tableName, string.Join(",", parameters));
        }

        public static string GetInsertQuery(string tableName, dynamic item)
        {
            PropertyInfo[] props = item.GetType().GetProperties();
            string[] columns = props.Select(p => p.Name).Where(s => s != "ID").ToArray();
            return string.Format("INSERT INTO {0} ({1}) OUTPUT inserted.ID VALUES (@{2})",
                                 tableName,string.Join(",", columns),string.Join(",@", columns));
        }

        public static QueryResult GetDynamicQuery<T>(string tableName, Expression<Func<T, bool>> expression)
        {
            List<QueryParameter> queryProperties = new List<QueryParameter>();
            try
            {
                BinaryExpression body = (BinaryExpression)expression.Body;
                WalkTree(body, ExpressionType.Default, ref queryProperties);
            }
            catch (Exception)
            {
                WalkTree(expression.Body, ExpressionType.Default, ref queryProperties);
            }
            IDictionary<string, object> expando = new ExpandoObject();
            StringBuilder builder = new StringBuilder();
            builder.Append("SELECT * FROM ");
            builder.Append($"{tableName} WITH(NOLOCK)");
            builder.Append(" WHERE ");
            for (int i = 0; i < queryProperties.Count; i++)
            {
                QueryParameter item = queryProperties[i];
                if (!string.IsNullOrEmpty(item.LinkingOperator) && i > 0)
                {
                    builder.Append(string.Format("{0} {1} {2} @{1} ", item.LinkingOperator, item.PropertyName,
                                                 item.QueryOperator));
                }
                else
                {
                    builder.Append(string.Format("{0} {1} @{0} ", item.PropertyName, item.QueryOperator));
                }
                expando[item.PropertyName] = item.PropertyValue;
            }
            return new QueryResult(builder.ToString().TrimEnd(), expando);
        }

        private static void WalkTree(Expression body, ExpressionType linkingType,
                             ref List<QueryParameter> queryProperties)
        {
            if (body is BinaryExpression)
            {
                var body2 = body as BinaryExpression;
                if (body2.NodeType != ExpressionType.AndAlso && body2.NodeType != ExpressionType.OrElse)
                {
                    string propertyName = GetPropertyName(body2);
                    object propertyValue = GetPropertyValue(body2.Right);
                    string opr = GetOperator(body.NodeType);
                    string link = GetOperator(linkingType);
                    queryProperties.Add(new QueryParameter(link, propertyName, propertyValue, opr));
                }
                else
                {
                    WalkTree(body2.Left, body.NodeType, ref queryProperties);
                    WalkTree(body2.Right, body.NodeType, ref queryProperties);
                }
            }
            if (body is MethodCallExpression)
            {
                var body2 = body as MethodCallExpression;
                string propertyName = body2.Object.ToString().Split(".").LastOrDefault();
                object propertyValue = body2.Arguments.FirstOrDefault();
                string link = GetOperator(linkingType);
                if (body2.Method.Name.Equals("Contains"))
                {
                    string val = propertyValue.ToString().Replace(((char)34).ToString(), "");
                    if (!val.Contains("%"))
                        queryProperties.Add(new QueryParameter(link, propertyName, $"%{val}%", "LIKE"));
                    else
                        queryProperties.Add(new QueryParameter(link, propertyName, $"{val}", "LIKE"));
                }
                if (body2.Method.Name.Equals("Equals"))
                {
                    string val = propertyValue.ToString().Replace(((char)34).ToString(), "");
                    queryProperties.Add(new QueryParameter(link, propertyName, val, "="));
                }
            }
        }

        private static object GetPropertyValue(Expression source)
        {
            ConstantExpression constantExpression = source as ConstantExpression;
            if (constantExpression != null)
            {
                return constantExpression.Value;
            }
            Expression<Func<object>> evalExpr = Expression.Lambda<Func<object>>(Expression.Convert(source, typeof(object)));
            Func<object> evalFunc = evalExpr.Compile();
            object value = evalFunc();
            return value;
        }

        private static string GetPropertyName(BinaryExpression body)
        {
            string propertyName = body.Left.ToString().Split(new char[] { '.' })[1];
            if (body.Left.NodeType == ExpressionType.Convert)
            {
                // hack to remove the trailing ) when convering.
                propertyName = propertyName.Replace(")", string.Empty);
            }
            return propertyName;
        }

        private static string GetOperator(ExpressionType type)
        {
            switch (type)
            {
                case ExpressionType.Equal:
                    return "=";

                case ExpressionType.NotEqual:
                    return "!=";

                case ExpressionType.LessThan:
                    return "<";

                case ExpressionType.GreaterThan:
                    return ">";

                case ExpressionType.AndAlso:
                case ExpressionType.And:
                    return "AND";

                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return "OR";

                case ExpressionType.Default:
                    return string.Empty;

                case ExpressionType.GreaterThanOrEqual:
                    return ">=";

                case ExpressionType.LessThanOrEqual:

                    return "<=";

                default:
                    throw new NotImplementedException();
            }
        }
    }

View Code

基於Dapper的DbContext :DapperDbContextBase

  public abstract class DapperDbContextBase : IDbContext
    {
        #region Constructors

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="connectString">連接字元串</param>
        protected DapperDbContextBase(string connectString)
        {
            ConnectString = connectString;
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        ///獲取 是否開啟事務提交
        /// </summary>
        public IDbTransaction CurrentTransaction { get; private set; }

        #endregion Properties

        #region Fields

        /// <summary>
        ///     當前資料庫連接
        /// </summary>
        public IDbConnection CurrentConnection =>
            TransactionEnabled ? CurrentTransaction.Connection : CreateConnection();

        /// <summary>
        ///     獲取 是否開啟事務提交
        /// </summary>
        public bool TransactionEnabled => CurrentTransaction != null;

        /// <summary>
        ///     連接字元串
        /// </summary>
        protected readonly string ConnectString;

        #endregion Fields

        #region Methods

        /// <summary>
        /// 顯式開啟數據上下文事務
        /// </summary>
        /// <param name="isolationLevel">指定連接的事務鎖定行為</param>
        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
        {
            if (!TransactionEnabled) CurrentTransaction = CreateConnection().BeginTransaction(isolationLevel);
        }

        /// <summary>
        ///     提交當前上下文的事務更改
        /// </summary>
        /// <exception cref="DataAccessException">提交數據更新時發生異常:" + msg</exception>
        public void Commit()
        {
            if (TransactionEnabled)
                try
                {
                    CurrentTransaction.Commit();
                }
                catch (Exception ex)
                {
                    if (ex.InnerException?.InnerException is SqlException sqlEx)
                    {
                        var msg = DataBaseHelper.GetSqlExceptionMessage(sqlEx.Number);
                        throw new DataAccessException("提交數據更新時發生異常:" + msg, sqlEx);
                    }

                    throw;
                }
        }

        /// <summary>
        ///     創建記錄
        /// </summary>
        /// <param name="entity">需要操作的實體類</param>
        /// <returns>操作是否成功</returns>
        public bool Create<T>(T entity)
            where T : ModelBase
        {
            return CurrentConnection.Insert(new List<T> { entity }, CurrentTransaction) > 0;
        }


        /// <summary>
        ///非同步創建記錄
        /// </summary>
        /// <param name="entity">需要操作的實體類</param>
        /// <returns>操作是否成功</returns>
        public async Task<bool> CreateAsync<T>(T entity)
            where T : ModelBase
        {
            var result = await CurrentConnection.InsertAsync(
                new List<T>
                {
                    entity
                }, CurrentTransaction);
            bool b = result > 0;
            return b;
        }

        /// <summary>
        ///創建資料庫連接IDbConnection
        /// </summary>
        /// <returns></returns>
        public abstract IDbConnection CreateConnection();

        /// <summary>
        ///     刪除記錄
        /// </summary>
        /// <returns>操作是否成功</returns>
        /// <param name="entity">需要操作的實體類.</param>
        public bool Delete<T>(T entity)
            where T : ModelBase
        {
            return CurrentConnection.Delete(entity);
        }
        /// <summary>
        /// 非同步刪除記錄
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public async Task<bool> DeleteAsync<T>(T entity)
            where T : ModelBase
        {
            var result = await CurrentConnection.DeleteAsync(entity);
            return result;
        }
        /// <summary>
        ///條件判斷是否存在
        /// </summary>
        /// <returns>是否存在</returns>
        /// <param name="predicate">判斷條件委託</param>
        public bool Exist<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            var result = CurrentConnection.ExecuteScalar(queryResult.Sql, (object)queryResult.Param, CurrentTransaction);
            return result != null;
        }
        /// <summary>
        /// 非同步判斷符合條件的實體是否存在
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public async Task<bool> ExistAsync<T>(Expression<Func<T, bool>> predicate = null)
          where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            var result = await CurrentConnection.ExecuteScalarAsync(queryResult.Sql, (object)queryResult.Param, CurrentTransaction);
            return result != null;
        }

        /// <summary>
        /// 根據id獲取記錄
        /// </summary>
        /// <returns>記錄</returns>
        /// <param name="id">id.</param>
        public T GetByKeyId<T>(object id)
            where T : ModelBase
        {
            return CurrentConnection.Get<T>(id, CurrentTransaction);
        }
        /// <summary>
        /// 非同步根據id獲取記錄
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<T> GetByKeyIdAsync<T>(object id)
            where T : ModelBase
        {
            var result = await CurrentConnection.GetAsync<T>(id, CurrentTransaction);
            return result;
        }

        /// <summary>
        ///條件獲取記錄集合
        /// </summary>
        /// <returns>集合</returns>
        /// <param name="predicate">篩選條件.</param>
        public List<T> GetList<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            return CurrentConnection.Query<T>(queryResult.Sql, (object)queryResult.Param, CurrentTransaction).ToList();
        }

        /// <summary>
        /// 條件獲取記錄集合(非同步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public async Task<List<T>> GetListAsync<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            var result = await CurrentConnection.QueryAsync<T>(queryResult.Sql, (object)queryResult.Param, CurrentTransaction);
            return result.ToList();
        }

        /// <summary>
        ///條件獲取記錄第一條或者默認
        /// </summary>
        /// <returns>記錄</returns>
        /// <param name="predicate">篩選條件.</param>
        public T GetFirstOrDefault<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            return CurrentConnection.QueryFirstOrDefault<T>(queryResult.Sql, (object)queryResult.Param, CurrentTransaction);
        }

        /// <summary>
        /// 條件獲取記錄第一條或者默認(非同步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public async Task<T> GetFirstOrDefaultAsync<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            var entity = await CurrentConnection.QueryFirstOrDefaultAsync<T>(queryResult.Sql, (object)queryResult.Param, CurrentTransaction);
            return entity;
        }

        /// <summary>
        /// 條件查詢
        /// </summary>
        /// <returns>IQueryable</returns>
        /// <param name="predicate">篩選條件.</param>
        public IQueryable<T> Query<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            var result = CurrentConnection.Query<T>(queryResult.Sql, (object)queryResult.Param, CurrentTransaction).ToList();
            return result.AsQueryable();
        }

        /// <summary>
        /// 條件查詢(非同步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public async Task<IQueryable<T>> QueryAsync<T>(Expression<Func<T, bool>> predicate = null)
            where T : ModelBase
        {
            var tableName = GetTableName<T>();
            var queryResult = DynamicQuery.GetDynamicQuery(tableName, predicate);
            var result = await CurrentConnection.QueryAsync<T>(queryResult.Sql, (object)queryResult.Param, CurrentTransaction);
            return result.AsQueryable();
        }

        /// <summary>
        ///顯式回滾事務,僅在顯式開啟事務後有用
        /// </summary>
        public void Rollback()
        {
            if (TransactionEnabled) CurrentTransaction.Rollback();
        }

        /// <summary>
        ///執行Sql 腳本查詢
        /// </summary>
        /// <param name="sql">Sql語句</param>
        /// <param name="parameters">參數</param>
        /// <returns>集合</returns>
        public IEnumerable<T> SqlQuery<T>(string sql, IDbDataParameter[] parameters)
        {
            var dataParameters = CreateParameter(parameters);
            return CurrentConnection.Query<T>(sql, dataParameters, CurrentTransaction);
        }

        /// <summary>
        ///執行Sql 腳本查詢(非同步)
        /// </summary>
        /// <param name="sql">Sql語句</param>
        /// <param name="parameters">參數</param>
        /// <returns>集合</returns>
        public async Task<IEnumerable<T>> SqlQueryAsync<T>(string sql, IDbDataParameter[] parameters)
        {
            var dataParameters = CreateParameter(parameters);
            var list = await CurrentConnection.QueryAsync<T>(sql, dataParameters, CurrentTransaction);
            return list;
        }

        /// <summary>
        /// 執行Sql 腳本查詢帶分頁(linq分頁)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public PagedList<T> SqlQueryAndPagedList<T>(string sql, IDbDataParameter[] parameters, int pageIndex, int pageSize)
        {
            var dataParameters = CreateParameter(parameters);
            var result = CurrentConnection.Query<T>(sql, dataParameters, CurrentTransaction);
            return PageHelper.ToPagedList(result.AsQueryable(), pageIndex, pageSize);
        }

        /// <summary>
        /// 帶分頁(伺服器端分頁)的自定義查詢
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="orderField"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="total"></param>
        /// <returns></returns>
        public IEnumerable<T> SqlQueryPage<T>(string sql, string orderField, int pageSize, int pageIndex, out int total) where T : class, new()
        {
            int num = (pageIndex - 1) * pageSize;
            int num1 = (pageIndex) * pageSize;
            orderField = "order by " + orderField;
            StringBuilder sb = new StringBuilder();
            sb.Append("Select * From (Select ROW_NUMBER() Over (" + orderField + ")");
            sb.Append(" As rowNum, * From (" + sql + ") As T ) As N Where rowNum > " + num + " And rowNum <= " + num1 + "");
            total = Convert.ToInt32(SqlQuery<int>("Select Count(1) From (" + sql + ") As t", null).FirstOrDefault());
            string last_sql = sb.ToString();
            var dataQuery = CurrentConnection.Query<T>(last_sql).ToList();
            return dataQuery;
        }

        /// <summary>
        /// FindObjectBase對象通用查詢帶分頁
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="orderField"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="total"></param>
        /// <returns></returns>
        public IEnumerable<T> SqlQueryPage<T>(T t, string orderField, int pageSize, int pageIndex, out int total) where T : FindObjectBase, new()
        {
            string tableName = GetQueryTableName<T>();
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT * FROM ");
            sb.Append($"{tableName} WITH(NOLOCK) ");
            sb.Append(" WHERE 1=1 AND ");
            var props = typeof(T).GetProperties().Where(p => !p.Name.StartsWith("Chk_"));
            foreach (var prop in props)
            {
                object obj = prop.GetValue(t, null);
                if (obj != null)
                {
                    if (prop.Name.ToUpper().StartsWith("LIKE_"))
                    {
                        sb.Append($"          {prop.Name.Replace("LIKE_", "")} LIKE {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("GT_"))
                    {
                        sb.Append($"          {prop.Name.Replace("GT_", "")} > {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("ST_"))
                    {
                        sb.Append($"          {prop.Name.Replace("ST_", "")} < {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("ST_Eq_"))
                    {
                        sb.Append($"          {prop.Name.Replace("ST_Eq_", "")} <= {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("GT_Eq_"))
                    {
                        sb.Append($"          {prop.Name.Replace("GT_Eq_", "")} >= {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("BETWEEN_"))
                    {
                        string[] array = obj.ToString().Split("|");
                        sb.Append($"          {prop.Name.Replace("GT_Eq_", "")} BETWEEN {array.FirstOrDefault()} AND {array.LastOrDefault()} AND");
                    }
                    else
                        sb.Append($"          {prop.Name}={obj} AND ");
                }
            }
            string sql = sb.ToString().ToUpper().TrimEnd("AND ".ToCharArray()); 
            int num = (pageIndex - 1) * pageSize;
            int num1 = (pageIndex) * pageSize;
            orderField = "order by " + orderField;
            StringBuilder builder = new();
            builder.Append("Select * From (Select ROW_NUMBER() Over (" + orderField + ")");
            builder.Append(" As rowNum, * From (" + sql + ") As T ) As N Where rowNum > " + num + " And rowNum <= " + num1 + "");
            total = Convert.ToInt32(SqlQuery<int>("Select Count(1) From (" + sql + ") As t", null).FirstOrDefault());
            string last_sql = builder.ToString();
            var dataQuery = CurrentConnection.Query<T>(last_sql).ToList();
            return dataQuery;
        }
        /// <summary>
        /// FindObjectBase對象通用查詢
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public IEnumerable<T> SqlQuery<T>(T t) where T : FindObjectBase, new()
        {
            string tableName = GetQueryTableName<T>();
            StringBuilder builder = new();
            builder.Append("SELECT * FROM ");
            builder.Append($"{tableName} WITH(NOLOCK) ");
            builder.Append(" WHERE 1=1 AND ");
            var props = typeof(T).GetProperties();
            foreach (var prop in props)
            {
                object obj = prop.GetValue(t, null);
                if (obj != null)
                {
                    builder.Append($" {prop.Name}={obj} AND ");
                }
            }
            string sql = builder.ToString().ToUpper().TrimEnd("AND".ToCharArray()); 
            var dataQuery = CurrentConnection.Query<T>(sql).ToList();
            return dataQuery;
        }

        /// <summary>
        /// 自定義查詢返回DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public DataTable SqlQueryReturnDataTable<T>(string sql, IDbDataParameter[] parameters)
        {
            var dataParameters = CreateParameter(parameters);
            var list = CurrentConnection.Query<T>(sql, dataParameters, CurrentTransaction).ToList();
            return ConvertExtension.ToDataTable(list);
        }

        /// <summary>
        /// 帶分頁(伺服器端分頁)的自定義查詢
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="orderField"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="total"></param>
        /// <returns></returns>
        public DataTable SqlQueryReturnDataTable<T>(string sql, string orderField, int pageSize, int pageIndex, out int total) where T : class, new()
        {
            int num = (pageIndex - 1) * pageSize;
            int num1 = (pageIndex) * pageSize;
            orderField = "order by " + orderField;
            StringBuilder sb = new StringBuilder();
            sb.Append("Select * From (Select ROW_NUMBER() Over (" + orderField + ")");
            sb.Append(" As rowNum, * From (" + sql + ") As T ) As N Where rowNum > " + num + " And rowNum <= " + num1 + "");
            total = Convert.ToInt32(SqlQuery<int>("Select Count(1) From (" + sql + ") As t", null).FirstOrDefault());
            string last_sql = sb.ToString();
            var list = CurrentConnection.Query<T>(last_sql).ToList();
            return ConvertExtension.ToDataTable(list);
        }

        /// <summary>
        ///更新實體類記錄
        /// </summary>
        /// <returns>操作是否成功.</returns>
        /// <param name="entity">實體類記錄.</param>
        public bool Update<T>(T entity)
            where T : ModelBase
        {
            return CurrentConnection.Update(entity, CurrentTransaction);
        }

        /// <summary>
        /// 更新實體類記錄(非同步)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public async Task<bool> UpdateAsync<T>(T entity)
            where T : ModelBase
        {
            return await CurrentConnection.UpdateAsync(entity, CurrentTransaction);
        }

        /// <summary>
        /// 構建Sql Parameter
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private DapperParameter CreateParameter(IDbDataParameter[] parameters)
        {
            if (!(parameters?.Any() ?? false)) return null;

            var dataParameters = new DapperParameter();
            foreach (var parameter in parameters) dataParameters.Add(parameter);
            return dataParameters;
        }

        /// <summary>
        /// 獲取實體的TableName
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private string GetTableName<T>()
            where T : ModelBase
        {
            var tableCfgInfo = AttributeHelper.Get<T,TableAttribute>();
            return tableCfgInfo != null ? tableCfgInfo.Name.Trim() : typeof(T).Name;
        }

        /// <summary>
        /// 獲取實體的TableName
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private string GetQueryTableName<T>()
            where T : FindObjectBase
        { 
            var tableCfgInfo = AttributeHelper.Get<T,TableAttribute>();
            return tableCfgInfo != null ? tableCfgInfo.Name.Trim() : typeof(T).Name;
        }


        /// <summary>
        ///執行與釋放或重置非託管資源關聯的應用程式定義的任務。
        /// </summary>
        public void Dispose()
        {
            if (CurrentTransaction != null)
            {
                CurrentTransaction.Dispose();
                CurrentTransaction = null;
            }

            CurrentConnection?.Dispose();
        }

        public List<T> GetList<T>(T t) where T : FindObjectBase
        {
            string tableName = GetQueryTableName<T>();
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT * FROM ");
            sb.Append($"{tableName} WITH(NOLOCK) ");
            sb.Append(" WHERE 1=1 AND ");
            var props = typeof(T).GetProperties().Where(p=>!p.Name.StartsWith("Chk_"));
            foreach (var prop in props)
            {
                object obj = prop.GetValue(t, null);
                if (obj != null)
                {
                    if (prop.Name.ToUpper().StartsWith("LIKE_"))
                    {
                        sb.Append($"          {prop.Name.Replace("LIKE_","")} LIKE {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("GT_"))
                    {
                        sb.Append($"          {prop.Name.Replace("GT_", "")} > {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("ST_"))
                    {
                        sb.Append($"          {prop.Name.Replace("ST_", "")} < {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("ST_Eq_"))
                    {
                        sb.Append($"          {prop.Name.Replace("ST_Eq_", "")} <= {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("GT_Eq_"))
                    {
                        sb.Append($"          {prop.Name.Replace("GT_Eq_", "")} >= {obj} AND ");
                    }
                    if (prop.Name.ToUpper().StartsWith("BETWEEN_"))
                    {
                        string[] array = obj.ToString().Split("|");
                        sb.Append($"          {prop.Name.Replace("GT_Eq_", "")} BETWEEN {array.FirstOrDefault()} AND {array.LastOrDefault()} AND");
                    }
                    else
                        sb.Append($"          {prop.Name}={obj} AND ");
                }
            }
            string sql = sb.ToString().ToUpper().TrimEnd("AND ".ToCharArray()); 
            return SqlQuery<T>(sql, null).ToList();
        }

        public DataTable SqlQueryReturnDataTable(string sql, IDbDataParameter[] parameters)
        {
            var list = CurrentConnection.Query(sql, parameters);
            return ConvertExtension.ToDataTable(list);
        } 

        #endregion Methods
    }

請原諒我沒有把這大段的程式碼收縮,如果你只是想跑起來看看,請忽略這些程式碼。後邊有下載鏈接。

/// <summary>
    /// 泛型倉儲
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DapperRepository<T> : IRepository
       where T : ModelBase
    {
        protected readonly DapperDbContextBase _dapperDbContext = null;
        protected readonly string _tableName = null;
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="dbContext"></param>
        public DapperRepository(IDbContext dbContext)
        {
            _dapperDbContext = (DapperDbContextBase)dbContext;
            TableAttribute tableCfgInfo = AttributeHelper.Get<T, TableAttribute>();
            _tableName = tableCfgInfo != null ? tableCfgInfo.Name.Trim() : typeof(T).Name;
        }
        /// <summary>
        /// 插入實體
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public bool Create<T1>(T1 entity) where T1 : ModelBase
        {
            return _dapperDbContext.Create(entity);
        }
        /// <summary>
        /// 插入多個實體
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public bool Create(IEnumerable<T> entities)
        {
            bool result = false;
            using (IDbConnection connection = _dapperDbContext.CreateConnection())
            {
                using (IDbTransaction transaction = connection.BeginTransaction())
                {
                    try
                    {
                        foreach (T item in entities)
                        {
                            connection.Insert(item, transaction);
                        }
                        transaction.Commit();
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 刪除實體
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public bool Delete<T1>(T1 entity) where T1 : ModelBase
        {
            return _dapperDbContext.Delete(entity);
        }
        /// <summary>
        /// 刪除多個實體
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public bool Delete(IEnumerable<T> entities)
        {
            bool result = false;
            using (IDbConnection connection = _dapperDbContext.CreateConnection())
            {
                using (IDbTransaction transaction = connection.BeginTransaction())
                {
                    try
                    {
                        foreach (T item in entities)
                        {
                            connection.Delete(item, transaction);
                        }
                        transaction.Commit();
                        result = true;
                    }
                    catch (Exception)
                    {
                        result = false;
                        transaction.Rollback();
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 檢測實體是否存在
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public bool Exist<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return _dapperDbContext.Exist(predicate); 
        }
        /// <summary>
        /// 用主鍵ID獲取實體
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public T1 GetByKeyId<T1>(object id) where T1 : ModelBase
        {
            return _dapperDbContext.GetByKeyId<T1>(id);
        }
        /// <summary>
        /// 根據實體ID獲取實體 id可能不是主鍵
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public T Get(object id)
        {
            using (IDbConnection connection = _dapperDbContext.CreateConnection())
            {
                return connection.Get<T>(id);
            }
        }
        /// <summary>
        /// 按條件獲取實體
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public List<T> Get(Expression<Func<T, bool>> predicate = null)
        {
            QueryResult queryResult = DynamicQuery.GetDynamicQuery(_tableName, predicate);
            using (IDbConnection connection = _dapperDbContext.CreateConnection())
            {
                return connection.Query<T>(queryResult.Sql, (T)queryResult.Param).ToList();
            }
        }
        /// <summary>
        /// 獲取符合條件的默認實體
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public T1 GetFirstOrDefault<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return _dapperDbContext.GetFirstOrDefault(predicate);
        }
        /// <summary>
        /// 獲取符合條件的集合
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public List<T1> GetList<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return _dapperDbContext.GetList(predicate);
        }
        /// <summary>
        /// 執行自定義查詢
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public IQueryable<T1> Query<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return _dapperDbContext.Query(predicate);
        }
        /// <summary>
        /// 修改實體
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public bool Update<T1>(T1 entity) where T1 : ModelBase
        {
            return _dapperDbContext.Update(entity);
        }

        public async Task<bool> DeleteAsync<T1>(T1 entity) where T1 : ModelBase
        {
            return await _dapperDbContext.DeleteAsync(entity);
        }

        public async Task<bool> ExistAsync<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return await _dapperDbContext.ExistAsync(predicate);
        }

        public async Task<T1> GetByKeyIdAsync<T1>(object id) where T1 : ModelBase
        {
            return await _dapperDbContext.GetByKeyIdAsync<T1>(id);
        }

        public async Task<List<T1>> GetListAsync<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return await _dapperDbContext.GetListAsync(predicate);
        }

        public async Task<T1> GetFirstOrDefaultAsync<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return await _dapperDbContext.GetFirstOrDefaultAsync(predicate);
        }

        public async Task<bool> CreateAsync<T1>(T1 entity) where T1 : ModelBase
        {
            return await _dapperDbContext.CreateAsync(entity);
        }

        public async Task<IQueryable<T1>> QueryAsync<T1>(Expression<Func<T1, bool>> predicate = default) where T1 : ModelBase
        {
            return await _dapperDbContext.QueryAsync(predicate);
        }

        public async Task<bool> UpdateAsync<T1>(T1 entity) where T1 : ModelBase
        {
            return await _dapperDbContext.UpdateAsync(entity);
        }

        public List<T1> GetList<T1>(T1 t) where T1 : FindObjectBase
        {
            return _dapperDbContext.GetList(t);
        }
    }

 為什麼要用Dapper呢?因為我們工廠的MES生產資料庫單表有十幾億記錄。如果你說這不是大數據,我就無語了。據我自己測試系統里的Radis快取沒有生效的情況下,資料庫也能硬抗!

 

 

我不相信別人吹上天的某某ORM組件,我只相信自己見過的!

成品截圖留念:

 

 

 

 

 

 

 

 

收穫與感想

 

  • 1、妥妥的吃了次螃蟹,收穫了經驗
  • 2、正在「為自己挖一口井」的路上
  • 3、動手寫一回持久層,收穫良多,終於搞清除ORM的原理
  • 4、源碼我是沒自信放到github的,後面會加上下載鏈接
  • 5、夥計們分享起來吧,這個生態建設任重而道遠啊。

下載源碼請猛擊這裡!