C# 使用Fluent API 创建自己的DSL
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的更多相关文章
- EF 学习系列二 数据库表的创建和表关系配置(Fluent API、Data Annotations、约定)
上一篇写了<Entity Farmework领域建模方式 3种编程方式>,现在就Code First 继续学习 1.数据库表的创建 新建一个MVC的项目,在引用右击管理NuGet程序包,点 ...
- 1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-firs ...
- 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 ...
- 使用 Fluent API 配置/映射属性和类型(摘自微软Data Access and Storage)
使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...
- DB表的关系及EF中Fluent API的使用
现在使用多数的数据库是关系型数据库,那么表与表之间的关系就会显得尤其重要,对于数据的CRUD处理和以后数据的分析有很大的好处.下面是对于数据库中对表关系的理解以及在EF中使用Fluent API来创建 ...
- Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)
在前面的随笔<Entity Framework 实体框架的形成之旅--Code First的框架设计(5)>里介绍了基于Code First模式的实体框架的经验,这种方式自动处理出来的模式 ...
- Entity Framework Code First (五)Fluent API - 配置关系
上一篇文章我们讲解了如何用 Fluent API 来配置/映射属性和类型,本文将把重点放在其是如何配置关系的. 文中所使用代码如下 public class Student { public int ...
- Entity Framework Code First (四)Fluent API - 配置属性/类型
上篇博文说过当我们定义的类不能遵循约定(Conventions)的时候,Code First 提供了两种方式来配置你的类:DataAnnotations 和 Fluent API, 本文将关注 Flu ...
- 使用Fluent API 配置/映射属性和类型
Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code F ...
随机推荐
- static关键字相关内容
静态变量(static)与非静态变量,静态方法(static)与非静态方法 //static public class Student { private static int age; //静态的变 ...
- 随笔:关于.net core单文件AOT编译
.Net Core单文件发布已经很流畅了(vs已支持图形化操作发布). 但类似Go或者Graalvm JDK的完全事前编译为本地机器码的红能功能,还未发布于.net 6特性中,还处于实验室中. 另外, ...
- [SCOI2009] windy 数 (数位dp)
题目 算法 应该是一道很经典的数位dp题 我们设dp[i][j]是填到第i位此时第i位的数是j的方案数 然后进行转移(代码注释) 代码 #include<iostream> #includ ...
- 洛谷 P5233 - [JSOI2012]爱之项链(Polya 定理+递推)
洛谷题面传送门 首先很明显题目暗示我们先求出符合条件的戒指数量,再计算出由这些戒指能够构成的项链的个数,因此考虑分别计算它们.首先是计算符合条件的戒指数量,题目中"可以通过旋转重合的戒指视作 ...
- Codeforces 739D - Recover a functional graph(二分图匹配)
Codeforces 题面传送门 & 洛谷题面传送门 首先假设我们已经填好了所有问号处的值怎样判断是否存在一个合法的构造方案,显然对于一种方案能够构造出合法的基环内向森林当且仅当: \(\fo ...
- python 新闻管理系统——启示
mysql取整函数: mysql函数ceil.floor.round mysql 取整 1.ceil() / ceiling() 向上取整 ex: ceil(1.2) = 2 2.floor() 向下 ...
- Spark的shuffle和MapReduce的shuffle对比
目录 MapperReduce的shuffle Spark的shuffle 总结 MapperReduce的shuffle shuffle阶段划分 Map阶段和Reduce阶段 任务 MapTask和 ...
- nodeJs-Stream接口
JavaScript 标准参考教程(alpha) 草稿二:Node.js Stream接口 GitHub TOP Stream接口 来自<JavaScript 标准参考教程(alpha)> ...
- openwrt装载固件
方法1. 确定串口号以后(在设备管理器可以查看) 打开SecureCRT软件,选择串口,设置合适的波特率(我用的115200),然后快速连接, 板子通电启动,在启动的时候会提示按任意键中断,这时按下任 ...
- JavaIO——内存操作流、打印流
我们之前所做的都是对文件进行IO处理,实则我们也可以对内存进行IO处理.我们将发生在内存中的IO处理称为内存流. 内存操作流也可分为两类:字节内存流和字符内存流. (1)ByteArrayInputS ...