为ABP框架添加基础集成服务

目录
  • 定义一个特性标记
  • 全局统一消息格式
    • Http状态码
    • 常用的请求结果
    • 响应模型
    • 全局异常拦截器
  • 先说明一下
    • ApiResponseModel是抽象类
    • 跨域请求
    • 配置API服务
  • 统一API模型验证消息
    • 创建前
    • 创建方式
    • 创建后
  • 补充:为什么需要统一格式

定义一个特性标记

这个标记用于标记一个枚举代表的信息。

在 AbpBase.Domain.Shared 项目,创建 Attributes目录,然后创建一个 SchemeNameAttribute 类,其内容如下:

    /// <summary>
    /// 标记枚举代表的信息
    /// </summary>
    [AttributeUsage(AttributeTargets.Field)]
    public class SchemeNameAttribute : Attribute
    {
        public string Message { get; set; }
        public SchemeNameAttribute(string message)
        {
            Message = message;
        }
    }

全局统一消息格式

为了使得 Web 应用统一响应格式以及方便编写 API 时有一个统一的标准,我们需要定义一个合适的模板。

在 AbpBase.Domain.Shared 创建一个Apis 目录。

Http 状态码

为了适配各种 HTTP 请求的响应状态,我们定义一个识别状态码的枚举。

在 Apis 目录,创建一个 HttpStateCode.cs 文件,其内容如下:

namespace AbpBase.Domain.Shared.Apis
{
    /// <summary>
    /// 标准 HTTP 状态码
    /// <para>文档地址<inheritdoc cref="https://www.runoob.com/http/http-status-codes.html"/></para>
    /// </summary>
    public enum HttpStateCode
    {
        Status412PreconditionFailed = 412,
        Status413PayloadTooLarge = 413,
        Status413RequestEntityTooLarge = 413,
        Status414RequestUriTooLong = 414,
        Status414UriTooLong = 414,
        Status415UnsupportedMediaType = 415,
        Status416RangeNotSatisfiable = 416,
        Status416RequestedRangeNotSatisfiable = 416,
        Status417ExpectationFailed = 417,
        Status418ImATeapot = 418,
        Status419AuthenticationTimeout = 419,
        Status421MisdirectedRequest = 421,
        Status422UnprocessableEntity = 422,
        Status423Locked = 423,
        Status424FailedDependency = 424,
        Status426UpgradeRequired = 426,
        Status428PreconditionRequired = 428,
        Status429TooManyRequests = 429,
        Status431RequestHeaderFieldsTooLarge = 431,
        Status451UnavailableForLegalReasons = 451,
        Status500InternalServerError = 500,
        Status501NotImplemented = 501,
        Status502BadGateway = 502,
        Status503ServiceUnavailable = 503,
        Status504GatewayTimeout = 504,
        Status505HttpVersionNotsupported = 505,
        Status506VariantAlsoNegotiates = 506,
        Status507InsufficientStorage = 507,
        Status508LoopDetected = 508,
        Status411LengthRequired = 411,
        Status510NotExtended = 510,
        Status410Gone = 410,
        Status408RequestTimeout = 408,
        Status101SwitchingProtocols = 101,
        Status102Processing = 102,
        Status200OK = 200,
        Status201Created = 201,
        Status202Accepted = 202,
        Status203NonAuthoritative = 203,
        Status204NoContent = 204,
        Status205ResetContent = 205,
        Status206PartialContent = 206,
        Status207MultiStatus = 207,
        Status208AlreadyReported = 208,
        Status226IMUsed = 226,
        Status300MultipleChoices = 300,
        Status301MovedPermanently = 301,
        Status302Found = 302,
        Status303SeeOther = 303,
        Status304NotModified = 304,
        Status305UseProxy = 305,
        Status306SwitchProxy = 306,
        Status307TemporaryRedirect = 307,
        Status308PermanentRedirect = 308,
        Status400BadRequest = 400,
        Status401Unauthorized = 401,
        Status402PaymentRequired = 402,
        Status403Forbidden = 403,
        Status404NotFound = 404,
        Status405MethodNotAllowed = 405,
        Status406NotAcceptable = 406,
        Status407ProxyAuthenticationRequired = 407,
        Status409Conflict = 409,
        Status511NetworkAuthenticationRequired = 511
    }
}

常用的请求结果

在相同目录,创建一个 CommonResponseType 枚举,其内容如下:

    /// <summary>
    /// 常用的 API 响应信息
    /// </summary>
    public enum CommonResponseType
    {
        [SchemeName("")] Default = 0,

        [SchemeName("请求成功")] RequstSuccess = 1,

        [SchemeName("请求失败")] RequstFail = 2,

        [SchemeName("创建资源成功")] CreateSuccess = 4,

        [SchemeName("创建资源失败")] CreateFail = 8,

        [SchemeName("更新资源成功")] UpdateSuccess = 16,

        [SchemeName("更新资源失败")] UpdateFail = 32,

        [SchemeName("删除资源成功")] DeleteSuccess = 64,

        [SchemeName("删除资源失败")] DeleteFail = 128,

        [SchemeName("请求的数据未能通过验证")] BadRequest = 256,

        [SchemeName("服务器出现严重错误")] Status500InternalServerError = 512
    }

响应模型

在 Apis 目录,创建一个 ApiResponseModel`.cs 泛型类文件,其内容如下:

namespace AbpBase.Domain.Shared.Apis
{
    /// <summary>
    /// API 响应格式
    /// <para>避免滥用,此类不能实例化,只能通过预定义的静态方法生成</para>
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    public abstract class ApiResponseModel<TData>
    {
        public HttpStateCode StatuCode { get; set; }
        public string Message { get; set; }
        public TData Data { get; set; }

        /// <summary>
        /// 私有类
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        private class PrivateApiResponseModel<TResult> : ApiResponseModel<TResult> { }
    }
}

StatuCode:用于说明此次响应的状态;

Message:响应的信息;

Data:响应的数据;

可能你会觉得这样很奇怪,先不要问,也不要猜,照着做,后面我会告诉你为什么这样写。

然后再创建一个类:

using AbpBase.Domain.Shared.Helpers;
using System;

namespace AbpBase.Domain.Shared.Apis
{
    /// <summary>
    /// Web 响应格式
    /// <para>避免滥用,此类不能实例化,只能通过预定义的静态方法生成</para>
    /// </summary>
    public abstract class ApiResponseModel : ApiResponseModel<dynamic>
    {
        /// <summary>
        /// 根据枚举创建响应格式
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="code"></param>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static ApiResponseModel Create<TEnum>(HttpStateCode code, TEnum enumType) where TEnum : Enum
        {
            return new PrivateApiResponseModel
            {
                StatuCode = code,
                Message = SchemeHelper.Get(enumType),
            };
        }

        /// <summary>
        /// 创建标准的响应
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <typeparam name="TData"></typeparam>
        /// <param name="code"></param>
        /// <param name="enumType"></param>
        /// <param name="Data"></param>
        /// <returns></returns>
        public static ApiResponseModel Create<TEnum>(HttpStateCode code, TEnum enumType, dynamic Data)
        {
            return new PrivateApiResponseModel
            {
                StatuCode = code,
                Message = SchemeHelper.Get(enumType),
                Data = Data
            };
        }

        /// <summary>
        /// 请求成功
        /// </summary>
        /// <param name="code"></param>
        /// <param name="Data"></param>
        /// <returns></returns>
        public static ApiResponseModel CreateSuccess(HttpStateCode code, dynamic Data)
        {
            return new PrivateApiResponseModel
            {
                StatuCode = code,
                Message = "Success",
                Data = Data
            };
        }

        /// <summary>
        /// 私有类
        /// </summary>
        private class PrivateApiResponseModel : ApiResponseModel { }
    }
}

同时在项目中创建一个 Helpers 文件夹,再创建一个 SchemeHelper 类,其内容如下:

using AbpBase.Domain.Shared.Attributes;
using System;
using System.Linq;
using System.Reflection;

namespace AbpBase.Domain.Shared.Helpers
{
    /// <summary>
    /// 获取各种枚举代表的信息
    /// </summary>
    public static class SchemeHelper
    {
        private static readonly PropertyInfo SchemeNameAttributeMessage = typeof(SchemeNameAttribute).GetProperty(nameof(SchemeNameAttribute.Message));

        /// <summary>
        /// 获取一个使用了 SchemeNameAttribute 特性的 Message 属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string Get<T>(T type)
        {
            return GetValue(type);
        }

        private static string GetValue<T>(T type)
        {
            var attr = typeof(T).GetField(Enum.GetName(type.GetType(), type))
                .GetCustomAttributes()
                .FirstOrDefault(x => x.GetType() == typeof(SchemeNameAttribute));

            if (attr == null)
                return string.Empty;

            var value = (string)SchemeNameAttributeMessage.GetValue(attr);
            return value;
        }
    }
}

上面的类到底是干嘛的,你先不要问。

全局异常拦截器

在 AbpBase.Web 项目中,新建一个 Filters 文件夹,添加一个 WebGlobalExceptionFilter.cs 文件,其文件内容如下:

using AbpBase.Domain.Shared.Apis;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using System.Threading.Tasks;

namespace ApbBase.HttpApi.Filters
{

    /// <summary>
    /// Web 全局异常过滤器,处理 Web 中出现的、运行时未处理的异常
    /// </summary>
    public class WebGlobalExceptionFilter : IAsyncExceptionFilter
    {

        public async Task OnExceptionAsync(ExceptionContext context)
        {
            if (!context.ExceptionHandled)
            {

                ApiResponseModel model = ApiResponseModel.Create(HttpStateCode.Status500InternalServerError,
                    CommonResponseType.Status500InternalServerError);
                context.Result = new ContentResult
                {
                    Content = JsonConvert.SerializeObject(model),
                    StatusCode = StatusCodes.Status200OK,
                    ContentType = "application/json; charset=utf-8"
                };
            }

            context.ExceptionHandled = true;

            await Task.CompletedTask;
        }
    }
}

然后 在 AbpBaseWebModule 模块的 ConfigureServices 函数中,加上:

            Configure<MvcOptions>(options =>
            {
                options.Filters.Add(typeof(WebGlobalExceptionFilter));
            });

这里我们还没有将写入日志,后面再增加这方面的功能。

先说明一下

前面我们定义了 ApiResponseModel 和其他一些特性还有枚举,这里解释一下原因。

ApiResponseModel 是抽象类

ApiResponseModel<T> 和 ApiResponseModel 是抽象类,是为了避免开发者使用时,直接这样用:

            ApiResponseModel mode = new ApiResponseModel
            {
                Code = 500,
                Message = "失败",
                Data = xxx
            };

首先这个 Code 需要按照 HTTP 状态的标准来填写,我们使用 HttpStateCode 枚举来标记,代表异常时,使用 Status500InternalServerError 来标识。

我非常讨厌一个 Action 的一个返回,就写一次消息的。

if(... ...)
	return xxxx("请求数据不能为空");

if(... ...)
	return xxxx("xxx 要大于 10");
... ..

这样每个地方一个消息说明,十分不统一,也不便于修改。

直接使用一个枚举来代表消息,而不能直接写出来,这样就可以达到统一了。

使用抽象类,可以避免开发者直接 new 一个,强制要求一定的消息格式来响应。后面可以进行更多的尝试,来体会我这样设计的便利性。

跨域请求

这里我们将配置 Web 全局允许跨域请求。

在 AbpBaseWebModule 模块中:

添加一个静态变量

private const string AbpBaseWebCosr = "AllowSpecificOrigins";

创建一个配置函数:

        /// <summary>
        /// 配置跨域
        /// </summary>
        /// <param name="context"></param>
        private void ConfigureCors(ServiceConfigurationContext context)
        {
            context.Services.AddCors(options =>
            {
                options.AddPolicy(AbpBaseWebCosr,
                    builder => builder.AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowAnyOrigin());
            });
        }

在 ConfigureServices 函数中添加:

            // 跨域请求
            ConfigureCors(context);

在 OnApplicationInitialization 中添加:

            app.UseCors(AbpBaseWebCosr);	// 位置在 app.UseRouting(); 后面

就这样,允许全局跨域请求就完成了。

配置 API 服务

你可以使用以下模块来配置一个 API 模块服务:

            Configure<AbpAspNetCoreMvcOptions>(options =>
            {
                options
                    .ConventionalControllers
                    .Create(typeof(AbpBaseHttpApiModule).Assembly, opts =>
                    {
                        opts.RootPath = "api/1.0";
                    });
            });

我们在 AbpBase.HttpApi 中将其本身用于创建一个 API 服务,ABP 会将继承了 AbpController 、ControllerBase 等的类识别为 API控制器。上面的代码同时将其默认路由的前缀设置为 api/1.0

也可以不设置前缀:

            Configure<AbpAspNetCoreMvcOptions>(options =>
            {                options.ConventionalControllers.Create(typeof(IoTCenterWebModule).Assembly);
            });

由于 API 模块已经在自己的 ConfigureServices 创建了 API 服务,因此可以不在 Web 模块里面编写这部分代码。当然,也可以统一在 Web 中定义所有的 API 模块。

统一 API 模型验证消息

创建前

首先,如果我们这样定义一个 Action:

        public class TestModel
        {
            [Required]
            public int Id { get; set; }

            [MaxLength(11)]
            public int Iphone { get; set; }

            [Required]
            [MinLength(5)]
            public string Message { get; set; }
        }

        [HttpPost("/T2")]
        public string MyWebApi2([FromBody] TestModel model)
        {
            return "请求完成";
        }

使用以下参数请求:

{
    "Id": "1",
    "Iphone": 123456789001234567890,
    "Message": null
}

会得到以下结果:

{
    "errors": {
        "Iphone": [
            "JSON integer 123456789001234567890 is too large or small for an Int32. Path 'Iphone', line 3, position 35."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|af964c79-41367b2145701111."
}

这样的信息阅读起来十分不友好,前端对接也会有一定的麻烦。

这个时候我们可以统一模型验证拦截器,定义一个友好的响应格式。

创建方式

在 AbpBase.Web 的项目 的 Filters 文件夹中,创建一个 InvalidModelStateFilter 文件,其文件内容如下:

using AbpBase.Domain.Shared.Apis;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;

namespace AbpBase.Web.Filters
{
    public static class InvalidModelStateFilter
    {
        /// <summary>
        /// 统一模型验证
        /// <para>控制器必须添加 [ApiController] 才能被此过滤器拦截</para>
        /// </summary>
        /// <param name="services"></param>
        public static void GlabalInvalidModelStateFilter(this IServiceCollection services)
        {
            services.Configure<ApiBehaviorOptions>(options =>
            {
                options.InvalidModelStateResponseFactory = actionContext =>
                {
                    if (actionContext.ModelState.IsValid)
                        return new BadRequestObjectResult(actionContext.ModelState);

                    int count = actionContext.ModelState.Count;
                    ValidationErrors[] errors = new ValidationErrors[count];
                    int i = 0;
                    foreach (var item in actionContext.ModelState)
                    {
                        errors[i] = new ValidationErrors
                        {
                            Member = item.Key,
                            Messages = item.Value.Errors?.Select(x => x.ErrorMessage).ToArray()
                        };
                        i++;
                    }

                    // 响应消息
                    var result = ApiResponseModel.Create(HttpStateCode.Status400BadRequest, CommonResponseType.BadRequest, errors);
                    var objectResult = new BadRequestObjectResult(result);
                    objectResult.StatusCode = StatusCodes.Status400BadRequest;
                    return objectResult;
                };
            });
        }

        /// <summary>
        /// 用于格式化实体验证信息的模型
        /// </summary>
        private class ValidationErrors
        {
            /// <summary>
            /// 验证失败的字段
            /// </summary>
            public string Member { get; set; }

            /// <summary>
            /// 此字段有何种错误
            /// </summary>
            public string[] Messages { get; set; }
        }
    }
}

在 ConfigureServices 函数中,添加以下代码:

            // 全局 API 请求实体验证失败信息格式化
            context.Services.GlabalInvalidModelStateFilter();

创建后

让我们看看增加了统一模型验证器后,同样的请求返回的消息。

请求:

{
    "Id": "1",
    "Iphone": 123456789001234567890,
    "Message": null
}

返回:

{
    "statuCode": 400,
    "message": "请求的数据未能通过验证",
    "data": [
        {
            "member": "Iphone",
            "messages": [
                "JSON integer 123456789001234567890 is too large or small for an Int32. Path 'Iphone', line 3, position 35."
            ]
        }
    ]
}

说明我们的统一模型验证响应起到了作用。

但是有些验证会直接报异常而不会流转到上面的拦截器中,有些模型验证特性用错对象的话,他会报错异常的。例如上面的 MaxLength ,已经用错了,MaxLength 是指定属性中允许的数组或字符串数据的最大长度,不能用在 int 类型上。大家测试一下请求下面的 json,会发现报异常。

{
    "Id": 1,
    "Iphone": 1234567900,
    "Message": "nullable"
}

以下是一些 ASP.NET Core 内置验证特性,大家记得别用错:

  • [CreditCard]:验证属性是否具有信用卡格式。 需要 JQuery 验证其他方法。
  • [Compare]:验证模型中的两个属性是否匹配。
  • [EmailAddress]:验证属性是否具有电子邮件格式。
  • [Phone]:验证属性是否具有电话号码格式。
  • [Range]:验证属性值是否在指定的范围内。
  • [RegularExpression]:验证属性值是否与指定的正则表达式匹配。
  • [Required]:验证字段是否不为 null。 有关此属性的行为的详细信息
  • [StringLength]:验证字符串属性值是否不超过指定长度限制。
  • [Url]:验证属性是否具有 URL 格式。
  • [Remote]:通过在服务器上调用操作方法来验证客户端上的输入。
  • [MaxLength ] MaxLength 是指定属性中允许的数组或字符串数据的最大长度

参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1

本系列第二篇到此,接下来第三篇会继续添加一些基础服务。

补充:为什么需要统一格式

首先,你看一下这样的代码:

在每个 Action 中,都充满了这种写法,每个相同的验证问题,在每个 Action 返回的文字都不一样,没有规范可言。一个人写一个 return,就加上一下自己要表达的 文字,一个项目下来,多少 return ?全是这种代码,不堪入目。

通过统一模型验证和统一消息返回格式,就可以避免这些情况。

源码地址:https://github.com/whuanle/AbpBaseStruct

本教程结果代码位置:https://github.com/whuanle/AbpBaseStruct/tree/master/src/2/AbpBase

到此这篇关于为ABP框架添加基础集成服务的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 搭建基础结构的ABP解决方案介绍

    目录 搭建项目基础结构 AbpBase.Domain.Shared 创建过程 AbpBase.Domain 创建过程 AbpBase.Application.Contracts 创建过程 AbpBase.Database 创建过程 AbpBase.Application 创建过程 AbpBase.HttpApi 创建过程 AbpBase.Web 创建过程 关于ABP和代码解疑 模块 [DependsOn] 配置服务和管道 模块如何关联 搭建项目基础结构 打开 VS 2019,创建一个解决方案,然

  • ABP框架中的事件总线功能介绍

    目录 事件总线 关于事件总线 为什么需要这个东西 事件总线创建过程 订阅事件 事件 发布事件 全局异常加入事件总线功能 创建事件 订阅事件 发布事件 测试 记录事件 事件总线 关于事件总线 ABP 中,为了方便进程间通讯,给开发者提供了一个叫 事件总线 的功能,事件总线分为 本地事件总线.分布式事件总线,本篇文章讲的是 本地事件总线,系列教程中暂时不考虑讲解 分布式事件总线. 事件总线 需要使用 Volo.Abp.EventBus 库,ABP 包中自带,不需要额外引入. 事件总线是通过 订阅-发

  • 为ABP框架配置数据库

    目录 创建标准的EFCore数据库上下文 连接字符串 定义隔离的上下文 多数据库支持和配置 Freesql配置服务 在 AbpBase.Database 中,通过 Nuget 添加以下几个库: 版本都是 1.9.0-preview0917,你可以使用最新版本的. Freesql FreeSql.Provider.Sqlite FreeSql.Provider.SqlServer FreeSql.Provider.MySql 创建标准的 EFCore 数据库上下文 在 ABP 中,EFCore 上

  • 为ABP框架增加日志组件与依赖注入服务

    目录 自动依赖注入 添加日志依赖 添加日志功能 依赖注入 自动依赖注入 在 AbpBase.Web 的 AbpBaseWebModule 中,添加一个函数: 此函数用于扫描模块中的服务,自动将其加入容器中,这样就不需要收到加入了. /// <summary> /// 自动扫描所有的服务并进行依赖注入 /// </summary> /// <param name="context"></param> private void Configu

  • 为ABP框架添加基础集成服务

    目录 定义一个特性标记 全局统一消息格式 Http状态码 常用的请求结果 响应模型 全局异常拦截器 先说明一下 ApiResponseModel是抽象类 跨域请求 配置API服务 统一API模型验证消息 创建前 创建方式 创建后 补充:为什么需要统一格式 定义一个特性标记 这个标记用于标记一个枚举代表的信息. 在 AbpBase.Domain.Shared 项目,创建 Attributes目录,然后创建一个 SchemeNameAttribute 类,其内容如下: /// <summary>

  • ABP框架的基础配置及依赖注入讲解

    配置ABP 配置是通过在自己模块的PreInitialize方法中来实现的 代码示例如下: public class SimpleTaskSystemModule : AbpModule { public override void PreInitialize() { //在你的应用中添加语言包,这个是英语和作者的土耳其语. Configuration.Localization.Languages.Add(new LanguageInfo("en", "English&quo

  • ABP框架的体系结构及模块系统讲解

    DDD分层 为了减少复杂性和提高代码的可重用性,采用分层架构是一种被广泛接受的技术. 为了实现分层的体系结构,ABP遵循DDD(领域驱动设计)的原则,将分为四个层次: 展现层(Presentation):提供一个用户界面,实现用户交互操作. 应用层(Application):进行展现层与领域层之间的协调,协调业务对象来执行特定的应用程序的任务.它不包含业务逻辑. 领域层(Domain):包括业务对象和业务规则,这是应用程序的核心层. 基础设施层(Infrastructure):提供通用技术来支持

  • 基于ASP.NET MVC的ABP框架入门学习教程

    为什么使用ABP 我们近几年陆续开发了一些Web应用和桌面应用,需求或简单或复杂,实现或优雅或丑陋.一个基本的事实是:我们只是积累了一些经验或提高了对,NET的熟悉程度. 随着软件开发经验的不断增加,我们发现其实很多工作都是重复机械的,而且随着软件复杂度的不断提升,以往依靠经验来完成一些简单的增删改查的做法已经行不通了.特别是用户的要求越来越高,希望添加的功能越来多,目前这种开发模式,已经捉襟见肘.我很难想象如何在现有的模式下进行多系统的持续集成并添加一些新的特性. 开发一个系统时,我们不可避免

  • 基于ABP框架实现RBAC(角色访问控制)

    在业务系统需求规划过程中,通常对于诸如组织机构.用户和角色等这种基础功能,通常是将这部分功能规划到通用子域中,这也说明了,对于这部分功能来讲,是系统的基石,整个业务体系是建立于这部分基石之上的,当然,还有诸如多语言.设置管理.认证和授权等.对于这部分功能,ABP中存在这些概念,并且通过Module Zero模块完成了这些概念. 一.角色访问控制之RBAC RBAC:Role Based Access Control,基于角色的访问控制,这在目前大多数软件中来讲已经算得上是普遍应用了,最常见的结构

  • 基于ABP框架实现数据字典开发

    在业务型的系统开发中,我们需要维护各种个样的类型,比如客户类型.客户行业.商品类型等等,这些类型往往信息量不多,并且相似度极高,如果采用一类型一表去设计,将会造成极大的工作量,通过将这部分类型的信息进行抽象,利用字段去存储类型区分,共用表结构,来达到兼容各种类型的功能,也就是设计一个数据字典,而对于一个具体类型来讲,是有多个选项的,比如性别,有男女,行业有工农商等,对于这部分选项,可抽象为某个类型下的字典项,即数据字典项. 一.数据字典设计思路 1.从客户类型.商品类型.行业类型来抽象考虑,首先

  • 详解ABP框架中的日志管理和设置管理的基本配置

    日志管理 Server side(服务器端) ASP.NET Boilerplate使用Castle Windsor's logging facility日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方便的处理各种特殊的日志库,而且当业务需要的时候,很容易替换日志组件. 译者注释:Castle是什么:Castle是针对.NET平台的一个开源项目,从数据访问框架ORM到

  • 解析ABP框架中的数据传输对象与应用服务

    数据传输对象(DTOs) 数据传输对象(Data Transfer Objects)用于应用层和展现层的数据传输. 展现层传入数据传输对象(DTO)调用一个应用服务方法,接着应用服务通过领域对象执行一些特定的业务逻辑并且返回DTO给展现层.这样展现层和领域层被完全分离开了.在具有良好分层的应用程序中,展现层不会直接使用领域对象(仓库,实体). 1.数据传输对象的作用: 为每个应用服务方法创建DTO看起来是一项乏味耗时的工作.但如果你正确使用它们,这将会解救你的项目.为啥呢? (1)抽象领域层 (

  • 详解ABP框架中的数据过滤器与数据传输对象的使用

    数据过滤器(Data filters) 在数据库开发中,我们一般会运用软删除(soft-delete)模式,即不直接从数据库删除数据,而是标记这笔数据为已删除.因此,如果实体被软删除了,那么它就应该不会在应用程序中被检索到.要达到这种效果,我们需要在每次检索实体的查询语句上添加SQL的Where条件IsDeleted = false.这是个乏味的工作,但它是个容易被忘掉的事情.因此,我们应该要有个自动的机制来处理这些问题. ABP提供数据过滤器(Data filters),它使用自动化的,基于规

随机推荐