DSL(Domain Specified Language)领域专用语言是描述特定领域问题的语言,听起来很唬人,其实不是什么高深的东西。看一下下面的代码:

using FlunetApiDemo;

var 张三 = "张三"
.是学生()
.身高(1.62M)
.体重(48M); Console.WriteLine(张三.BMI());
Console.WriteLine(张三.BMI状态());

这段代码根据学生的身高体重,计算BMI并判断状态(偏瘦、正常、超重还是肥胖),看到这里,各位同学可能已经发现问题了:学生有小学生、中学生和大学生,难道计算算法一样?男生女生的计算算法也一样?在这个问题中,各位都是领域专家,从我写的描述特定问题的代码中发现了问题,我需要对代码进行修改,增加年龄和性别因素。

从上面的例子可以看到DSL的作用:是解决领域专家与软件开发人员之间的沟通问题。领域专家通常不懂得编程,无法判断开发人员写的代码是否符合领域的要求,只能是等到软件编写完成,从软件运行表现出来的功能进行判断,而这时成本已经发生了,几个来回下来,进度超时,成本超支。DSL使用领域相关的术语编写,领域专家可以理解,而语言本身基于某种宿主语言,比如C#,可以编译运行,容易验证。所以恰当的DSL可以打通领域专家和开发人员之间的障碍,使软件的业务核心部分开发可靠并有效率。“可以执行”是DSL与需求阶段使用的伪语言或者带图示的自然语言最大的不同。在需求描述的时候,经常使用各种图示或者伪语言对业务进行描述,伪语言一般是一种类似的结构化语言,这种貌似语言的东西往往是很有害的,因为只是大概描述了过程,很多实现细节被忽略或者隐藏了。由于不是严格的编程语言,无法生成可执行的代码,所以也就无法验证对错。

结合上面的例子,我们看一下如何使用Fluent Api创建自己的DSL。其使用的技术实质上是实现现有类型的扩展,这需要我们1)声明一个static类,2)在类中使用static函数,3)使用this关键字修饰需要扩展的类型。上面的"张三".是学生(),“是学生”是字符串类型的一个扩展,返回的是自己定义的Student类型,这段代码如下:

namespace FlunetApiDemo
{
public static class FluentExt
{
public static Student 是学生(this string name)
{
return new Student { Name = name };
} public static Student 身高(this Student student,decimal height)
{
student.Height = height;
return student;
} public static Student 体重(this Student student, decimal weight)
{
student.Weight = weight;
return student;
} public static decimal BMI(this Student student)
{
return student.Weight / student.Height / student.Height;
} public static string BMI状态(this Student student)
{
var bmi=student.BMI();
if (bmi > 24) return "肥胖";
if (bmi > 21) return "超重";
if (bmi < 15) return "偏瘦";
return "正常";
}
}
}

在Student类中只定义关键属性:

namespace FlunetApiDemo
{
public class Student
{
public string Name { get; set; }=string.Empty; public decimal Height { get; set; } public decimal Weight { get; set; }
public override string ToString()
{
return Name;
}
}
}

怎么样,挺简单的吧。完整的代码上传到github: https://github.com/zhenl/FlunetApiDemo

最后的问题是代码中的中文问题,我的原则是怎么方便怎么来,通常我们编写程序时不主张使用中文作为变量或者方法名称,尽管现代编程语言的编译器很多已经不限于只支持ASCII码,但我们仍然无法确保在某些情况下不出现问题(比如如果将中文命名的方法映射为Web Api接口,不支持中文的客户端可能无法调用这个Api)。然而作为领域特定语言的DSL就不用有这个限制,DSL的主要目的就是沟通,如果必须用英文或者汉语拼音进行编写,效果就会大打折扣,更不用说很多领域都是中文为主的,这里不展开说了,举几个例子,“唐诗”、“宋词”、“元曲”估计翻成英语领域专家和程序员都看不懂。

C# 使用Fluent API 创建自己的DSL的更多相关文章

  1. EF 学习系列二 数据库表的创建和表关系配置(Fluent API、Data Annotations、约定)

    上一篇写了<Entity Farmework领域建模方式 3种编程方式>,现在就Code First 继续学习 1.数据库表的创建 新建一个MVC的项目,在引用右击管理NuGet程序包,点 ...

  2. 1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-firs ...

  3. ORM系列之二:EF(4) 约定、注释、Fluent API

    目录 1.前言 2.约定 2.1 主键约定 2.2 关系约定 2.3 复杂类型约定 3.数据注释 3.1 主键 3.2 必需 3.3 MaxLength和MinLength 3.4 NotMapped ...

  4. 使用 Fluent API 配置/映射属性和类型(摘自微软Data Access and Storage)

    使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...

  5. DB表的关系及EF中Fluent API的使用

    现在使用多数的数据库是关系型数据库,那么表与表之间的关系就会显得尤其重要,对于数据的CRUD处理和以后数据的分析有很大的好处.下面是对于数据库中对表关系的理解以及在EF中使用Fluent API来创建 ...

  6. Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)

    在前面的随笔<Entity Framework 实体框架的形成之旅--Code First的框架设计(5)>里介绍了基于Code First模式的实体框架的经验,这种方式自动处理出来的模式 ...

  7. Entity Framework Code First (五)Fluent API - 配置关系

    上一篇文章我们讲解了如何用 Fluent API 来配置/映射属性和类型,本文将把重点放在其是如何配置关系的. 文中所使用代码如下 public class Student { public int ...

  8. Entity Framework Code First (四)Fluent API - 配置属性/类型

    上篇博文说过当我们定义的类不能遵循约定(Conventions)的时候,Code First 提供了两种方式来配置你的类:DataAnnotations 和 Fluent API, 本文将关注 Flu ...

  9. 使用Fluent API 配置/映射属性和类型

    Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code F ...

随机推荐

  1. Kubernetes Pod 全面知识

    Pod 是在 Kubernetes 中创建和管理的.最小的可部署的计算单元,是最重要的对象之一.一个 Pod 中包含一个或多个容器,这些容器在 Pod 中能够共享网络.存储等环境. 学习 Kubern ...

  2. nginx_location

    Nginx location 配置语法 1. location [ = | ~ | ~* | ^~ ] uri { ... } 2. location @name { ... } location 配 ...

  3. Prometheus_exporter安装与使用

    Promethues概述:可以看一下更详细的介绍,以下为转载的博客,原文链接,支持原创,请多多支持!!:闫世成的博客园 Prometheus-node-exporter 1.简介: 内核公开的硬件和操 ...

  4. Python | 迭代器与zip的一些细节

    首先抛出一个困扰本人许久的问题: nums = [1,2,3,4,5,6] numsIter = iter(nums) for _ in zip(*[numsIter]*3): print(_) pr ...

  5. IPFS是什么?IPFS原理、IPFS存储

    以下内容调研截止到2021/11/5日 IPFS简介 IPFS是一种内容可寻址.点对点.分布式文件系统.IPFS采用内容-地址寻址技术,即通过文件内容进行检索而不是通过文件的网络地址.简单来说,就是对 ...

  6. 学习java的第六天

    一.今日收获 1.开始了学习手册第二章的学习 2.了解了java里的常量与变量以及数据类型,与c语言的内容类似 二.今日难题 1.都是基础知识,没有什么难题 三.明日目标 1.继续学习java学习手册 ...

  7. Notepad++【远程操作linux文件】

    目录 目的 预期效果 操作步骤 1.打开插件 2.安装NppFTP 3.连接远程主机 注意 目的 通过Notepad++远程登录linux主机,修改配置文件 预期效果 在Notepad++上登录lin ...

  8. ES6必知,箭头函数与普通函数的区别。

    1. 箭头函数没有prototype(原型),所以箭头函数本身没有this let a = () =>{}; console.log(a.prototype); // undefined 2. ...

  9. 【leetcode】337. House Robber III

    The thief has found himself a new place for his thievery again. There is only one entrance to this a ...

  10. 因Console.Read()导致Centos 后台运行.net core程序报错

    .net 控制台程序通常用 Console.Read(),或者Console.ReadKey()让进程阻塞保持,不退出. 但在.net core 需要将程序放在后台执行时 用Console.Read( ...