Asp.net 面向接口可扩展框架之业务规则引擎扩展模块

随着面向接口可扩展框架的继续开发,有些功能开发出现了"瓶颈",有太多的东西要写死才好做。但写死的代码扩展性是非常的不好,迷茫中寻找出入...

进而想到我以前开发的好几个项目,都已有一定的可配置能力,想想怎么把这些地方的代码抽象提取出来。进而想到"业务规则引擎",网上找了几个都不太入"眼",就抽时间再造个"轮子"

业务规则引擎在很多成熟的工作流引擎中都有相应的模块,是工作流的核心之一。但是除了工作流很多业务都需要业务规则引擎,所以它非常有必要独立作为一个模块。

在现实中很多项目开发都需要一些定制化的分支需求,为了这些需求把项目搞的鸡飞狗跳。使用业务规则引擎来做逻辑分支路由、参数“矫正”、拦截等,说是如虎添翼应该不为过。



这里使用社区发文章来做个例子

1、先看文章相关模型

A:ArticleRepository是文章数据仓储,实际是把文章存放在内存的List中

B:User是文章作者,Article就是文章了,ArticlePublish是DTO,包含文章和作者的信息

2、文章有不同的分类

        ArticleRepository TopArticles = new ArticleRepository { Name = "置顶" };
        ArticleRepository PerfectArticles = new ArticleRepository { Name = "精华" };
        ArticleRepository NetArticles = new ArticleRepository { Name = ".Net" };
        ArticleRepository OtherArticles = new ArticleRepository { Name = "其他" };

A:其中分为.net文章和其他文章,另外还有“置顶”和“精华”两个推送分类

B:文章现在分为.net文章和其他文章两个分类,以后很难说不用增加Java、PHP等其他分类,所以这个地方需要可扩展

C:不是每个人发的文章都置顶和精华,要不就没法愉快的玩耍了

3、可以按授权进行推送

先看授权相关建模

为了简单明了,这里都是用内存对象来模拟存储

A:Role是角色,角色有个权重的字段(Sort),Sort值越大权限越大

(有人说角色不应该是平等的吗?通过绑定资源来控制权限?我想问国家主席和你们村主任能平等吗?角色特权和资源绑定等手段应该综合来用。再者我们是演示业务规则引擎的,不是专门讨论权限系统的,先打住)

B:RolePermission用于授权(Grant)和获取权限(判断权限),维护者一个用户和权限的关联关系

    public class RolePermission : IEntityAccess<User, Role>, IComparer<Role>
    {
        private Dictionary<User, Role> _permission = new Dictionary<User, Role>();
        public bool Grant(User user, Role role)
        {
            Role role0 = Get(user);
            if (role0 != null && role0.Sort >= role.Sort)
                return true;
            _permission[user] = role;
            return true;
        }
        public Role Get(User user)
        {
            Role role = null;
            _permission.TryGetValue(user, out role);
            return role;
        }
        int IComparer<Role>.Compare(Role x, Role y)
        {
            return Comparer<Role>.Default.Compare(x, y);
        }
    }

RolePermission

4、继续场景设置

        Role manager = new Role { Id = 1, Name = "管理员", Sort = 9999 };
        Role expert = new Role { Id = 2, Name = "专家", Sort = 999 };
        User user1 = new User { Id = 1, Name = "张三", Year = 3 };
        User user2 = new User { Id = 2, Name = "李四", Year = 10 };
        User user3 = new User { Id = 3, Name = "王二", Year = 0 };

5、现在可以开始发文章了

            RolePermission permission = new RolePermission();
            ConfigRole(permission);
            ArticlePublish post1 = new ArticlePublish(user1, new Article { Content = ".Net" });
            ArticlePublish post2 = new ArticlePublish(user2, new Article { Content = "Java" });
            ArticlePublish post3 = new ArticlePublish(user3, new Article { Content = "Php" });
            Engine<ArticlePublish, int> engine = new Engine<ArticlePublish, int>();
            ConfigCategory(engine);
            Post(engine, post1, post2, post3);
            Show(TopArticles, PerfectArticles, NetArticles, OtherArticles);

        private void ConfigRole(RolePermission permission)
        {
            permission.Grant(user3, expert);
        }

授权代码

        private static void Post(Engine<ArticlePublish, int> engine, params ArticlePublish[] articles)
        {
            foreach (var item in articles)
            {
                int id = 0;
                if (engine.Run(item, ref id))
                    Console.WriteLine(string.Concat("文章处理成功,Id=", id.ToString()));
            }
        }

发表文章代码

        private static void Show(params ArticleRepository[] repositorys)
        {
            foreach (var repository in repositorys)
            {
                Console.WriteLine(new string(‘-‘, 80));
                List<Article> list = repository.ListAll();
                if (list.Count < 1)
                {
                    Console.WriteLine(string.Concat(repository.Name, " 无"));
                    continue;
                }
                Console.WriteLine(repository.Name);
                foreach (var item in list)
                {
                    Console.WriteLine(string.Concat("Article{Id=", item.Id, ",Content=", item.Content, "}"));
                }
                Console.WriteLine(new string(‘-‘, 80));
            }
        }

显示所有文章代码

文章处理成功,Id=1
文章处理成功,Id=2
文章处理成功,Id=3
--------------------------------------------------------------------------------

置顶 无
--------------------------------------------------------------------------------

精华 无
--------------------------------------------------------------------------------

.Net
Article{Id=1,Content=.Net}
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

其他
Article{Id=2,Content=Java}
Article{Id=3,Content=Php}
--------------------------------------------------------------------------------

三篇文章发表成功,一篇.net,两篇其他,效果非常不错

先等等,Engine<ArticlePublish, int>是什么鬼,要发文章不应该是ArticleRepository吗?

Engine就是大名鼎鼎的业务规则引擎了,ArticleRepository发表文章不假,但是都是由Engine决定发不发,用谁发,这些就是业务规则,

(ArticleRepository就是只负责存储和读取,职责非常单一)

6、把业务规则定义看一下

        private void ConfigCategory(Engine<ArticlePublish, int> engine)
        {
            engine.When(post => post.Article.Content.Contains(".Net")).Then(post => NetArticles.Add(post.Article));
            engine.Then(post => OtherArticles.Add(post.Article));
        }

非常简单,如果文章包含.net关键字,使用NetArticles存储,否则使用OtherArticles存储(分表就是这么简单!!!)

7、继续推送的例子

            Engine<ArticlePublish, int> pushEngine = new Engine<ArticlePublish, int>();
            ConfigPush(permission, pushEngine);
            ConfigYear(pushEngine);
            Post(pushEngine, post1, post2, post3);
            Show(TopArticles, PerfectArticles, NetArticles, OtherArticles);
文章处理成功,Id=2
文章处理成功,Id=3
--------------------------------------------------------------------------------

置顶
Article{Id=3,Content=Php}
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

精华
Article{Id=2,Content=Java}
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

.Net
Article{Id=1,Content=.Net}
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

其他
Article{Id=2,Content=Java}
Article{Id=3,Content=Php}
--------------------------------------------------------------------------------

这次在置顶和精华都各有一篇了

8、我们看一下推送规则是怎么定义的

        private void ConfigPush(RolePermission permission, Engine<ArticlePublish, int> engine)
        {
            int topNum = 0;
            int topLimit = 1;
            engine.When(post => topNum < topLimit && Comparer<Role>.Default.Compare(permission.Get(post.User), expert) >= 0).Then(post => { topNum++; return TopArticles.Add(post.Article); });
            engine.When(post => Comparer<Role>.Default.Compare(permission.Get(post.User), expert) >= 0).Then(post => PerfectArticles.Add(post.Article));
        }
        private void ConfigYear(Engine<ArticlePublish, int> engine)
        {
            engine.When(post => post.User.Year >= 8).Then(post => PerfectArticles.Add(post.Article));
        }

解读一下

A:if 专家及以上权限且可以发置顶,推送到置顶(先来先得)

B:else if 专家发的文章推送到精华

C:else if 8年以上会员发的文章推送到精华

D:else 什么都不做

注:先不要和我掰扯以上业务规则的合理性,只是个测试例子而已

就是这么简单,老板再也不用担心我不会写业务规则了

以上都是使用Fluent代码来做业务规则配置的,以后我还需要做使用文件配置做动态业务规则的例子,以便在容器配置文件中使用

时间: 2024-09-26 20:23:50

Asp.net 面向接口可扩展框架之业务规则引擎扩展模块的相关文章

Asp.net 面向接口可扩展框架之使用“类型转化基础服务”测试四种Mapper(AutoMapper、EmitMapper、NLiteMapper及TinyMapper)

Asp.net 面向接口可扩展框架的“类型转化基础服务”是我认为除了“核心容器”之外最为重要的组成部分 但是前面博文一出,争议很多,为此我再写一篇类型转化基础服务和各种Mapper结合的例子,顺便对各种Mapper做个简单的优缺点对比 我对第三方组件评介有三个标准,一.可用性,二.性能,三.易用性 本例子中四个四种Mapper以前我都没使用过(因为以前我都用自己的Mapper),本次测试可能不准确,错误的地方请大家指正 AutoMapper使用的是4.2.1.0,需要.net4.5支持(我使用N

Asp.net 面向接口可扩展框架之消息队列组件

消息队列对大多数人应该比较陌生.但是要提到MQ听说过的人会多很多.MQ就是英文单词"Message queue"的缩写,翻译成中文就是消息队列(我英语差,翻译错了请告知). PS:话说国人熟悉MQ比消息队列多,是不是因为国人的外语水平高于国语水平好几个数量级 1.看一下度娘怎么解释消息队列 参考链接:消息队列_百度百科 度娘解释消息队列是在两台计算机间传输的,套句很时髦的说法就是用来做分布式传输的,是个很高大上的东西 2.我的看法稍有不同 我更追溯到“消息队列”的字面“本源”的意思.我

Asp.net 面向接口可扩展框架之数据处理模块及EntityFramework扩展和Dapper扩展(含干货)

面向接口数据处理模块是什么意思呢?实际上很简单,就是使用面向接口的思想和方式来做数据处理. 还提到EntityFramework和Dapper,EntityFramework和Dapper是.net环境下推崇最高的两种ORM工具. 1.EntityFramework是微软出的根正苗红的.netd的ORM工具,直接在Vs工具和Mvc框架中集成了,默认生成的项目就是使用EntityFramework的;微软也一直都在维护更新升级,最新版本最新版本都在EF7了.也迁移到了最新的.net Core平台了

Asp.net 面向接口可扩展框架之类型转化基础服务

新框架正在逐步完善,可喜可贺的是基础服务部分初具备模样了,给大家分享一下 由于基础服务涉及太广,也没开发完,这篇只介绍其中的类型转化部分,命名为类型转化基础服务,其实就是基础服务模块的类型转化子模块 说到类型转化必须要清楚.net的类型,类型都不清楚何来类型转化 1.Primitive类型 1.1 这个概念估计很多人都没听说过,Primitive不是一个新类型,而是.net类型中最基本的一种分类,是基元类型的意思       MS将类型分为三类:Primitive(基元类型).Complex(复

Asp.net 面向接口可扩展框架之“Mvc扩展框架及DI”

标题“Mvc扩展框架及DI”有点绕口,我也想不出好的命名,因为这个内容很杂,涉及多个模块,但在日常开发又密不可分 首先说Mvc扩展框架,该Mvc扩展就是把以前的那个Mvc分区扩展框架迁移过来,并优化整合了一下 一.Mvc扩展框架主要功能: 1.Mvc的依赖注入(DI)功能(类MvcDependency) 依赖IContainerFactory接口,不再依赖具体容器 2.Mvc全局过滤器(GlobalFilterProvider) 配置在Mvc的依赖注入容器中就能自动易用上,其实逻辑很简单,就是继

面向接口可扩展框架

Asp.net 面向接口可扩展框架之核心容器(含测试代码下载) 新框架的容器部分终于调通了!容器实在太重要了,所以有用了一个名词叫“核心容器”. 容器为什么那么重要呢?这个有必要好好说道说道. 1.首先我们从框架名称面向接口编程说起,什么是面向接口编程?(这个度娘回答一下) 解读一下:类是个体的定义(建模), 个体的每一方面都可以是一个接口 说白点,其一接口可以代表对象(类)一个方面,再说透点对象可能是多面手(继承多个接口),能在不同场景(作为不同接口的实例)下正常工作 其二每个接口可以有不同实

面向接口可扩展框架之“Mvc扩展框架及DI”

面向接口可扩展框架之“Mvc扩展框架及DI” 标题“Mvc扩展框架及DI”有点绕口,我也想不出好的命名,因为这个内容很杂,涉及多个模块,但在日常开发又密不可分 首先说Mvc扩展框架,该Mvc扩展就是把以前的那个Mvc分区扩展框架迁移过来,并优化整合了一下 一.Mvc扩展框架主要功能: 1.Mvc的依赖注入(DI)功能(类MvcDependency) 依赖IContainerFactory接口,不再依赖具体容器 2.Mvc全局过滤器(GlobalFilterProvider) 配置在Mvc的依赖注

Asp.net 面向接口框架之应用程序上下文作用域组件

在团队中推广面向接口开发两年左右,成果总体来说我还是挺满意的,使用面向接口开发的模块使用Unity容器配置的功能非常稳定也很好扩展. 但是由于当时开发的匆忙(边开发边应用),留下一些比较致命的问题: 1.很多接口定义的不合理,通用性和扩展性不好 2.固定死了使用Unity容器,如果更大面积推广有问题,有些人已经很熟悉其他容器了,再来重新学Unity没有必要 3.配置比较麻烦,需要简化 所以我觉得有必要重新开发一个框架,对原框架取其精华去其糟粕,再吸收开源项目(含微软开放源代码的部分),争取做出一

Asp.net 面向接口框架之核心容器

新框架的容器部分终于调通了!容器实在太重要了,所有用了一个名词叫“核心容器”. 容器为什么那么重要呢?这个有必要好好说道说道. 1.首先我们说从框架名称面向接口编程说起,什么是面向接口编程?(这个度娘回答一下) 解读一下:类是个体的定义(建模), 个体的每一方面都可以是一个接口 说白点,其一接口可以代表对象(类)一个方面,再说透点对象可能是多面手(继承接口),能在不同场景(作为不同接口的实例)工作 其二每个接口可以不同实现,只要实现了这个接口,基本上就可以替换这个位置来正常工作 2.我觉得面向接