by 魏琼东
2014.8.21 11:24
一、分布式消息总线以及基于Socket的实现
在前面的分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载一文之中给大家分享和介绍了一个极其简单也非常容易上的基于.NET Socket Tcp 技术实现的分布消息总线,也是一个简单的发布订阅框架:
并且以案例的形式为大家演示了如何使用这个分布式消息总线架构发布订阅架构模式的应用程序,在得到各位同仁的反馈的同时,大家也非常想了解订阅者离线的情况,即支持离线构发布订阅框架。
二、离线架构
不同于订阅者、发布者都同时在线的情况,支持订阅者离线,架构将有所变化,如下图所示:
也会比原先的结构将更加复杂,其中需要处理以下两个关键点:
1)订阅者的持久化存储。
2)订阅者离线之后其所订阅消息的持久存储。
三、解决方案
为解决消息总线的离线支持机制,我们在Socket 框架之中增加了一个接口ISubscribeStorager:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace EAS.Messages
7: {
8: /// <summary>
9: /// 消息订阅存储接口。
10: /// </summary>
11: public interface ISubscribeStorager
12: {
13: /// <summary>
14: /// 持久化订阅。
15: /// </summary>
16: /// <param name="subscriber">订阅者。</param>
17: /// <param name="topic">消息主题。</param>
18: void Subscribe(string subscriber, string topic);
19:
20: /// <summary>
21: /// 持久化退订。
22: /// </summary>
23: /// <param name="subscriber">订阅者。</param>
24: /// <param name="topic">消息主题。</param>
25: void Unsubscribe(string subscriber, string topic);
26:
27: /// <summary>
28: /// 装载订阅信息。
29: /// </summary>
30: /// <returns>系统之中的订阅清单。</returns>
31: List<SubscribeItem> LoadSubscribes();
32:
33: /// <summary>
34: /// 写入消息。
35: /// </summary>
36: /// <param name="subscriber">订阅者。</param>
37: /// <param name="message">消息对象。</param>
38: void Write(string subscriber, QueueMessage message);
39:
40: /// <summary>
41: /// 读消息。
42: /// </summary>
43: /// <param name="subscriber">订阅者。</param>
44: /// <param name="message">消息对象。</param>
45: /// <returns>成功读取返回true,否则返回false。</returns>
46: bool Read(string subscriber, out QueueMessage message);
47: }
48: }
ISubscribeStorager共提供持久化订阅持久化消息存储共五个函数,其中:
LoadSubscribes:服务端初始化时读取所有的离线订阅关系,即那个订阅都订阅那那个主题。
Subscribe:持久化订阅者,当订阅才上线订阅消息时,持久化订阅关系,供离线检测之用。
Unsubscribe:持久化取消订阅,当订阅者退订消息时,从持久化订阅关系之中删除。
Write:当订阅者离线时,把订阅消息写入持久化存储。
Read:当离线订阅者上线时,从持久存储之中读取一条消息向其发送。
ISubscribeStorager:可以选择自己实现这个接口,以建立满足自己规则的离线存储机制,当然在AgileEAS.NET SOA 中间件之中提供了两种离线存储机制,存储于数据库和存储于MSMQ,下面向大家介绍一下这两种内置实现。
四、两种内置离线存储机制
在AgileEAS.NET SOA 中间件平台之中提供了两个ISubscribeStorager的实现,基于数据库的离线订阅存储实现EAS.Messages.DbSubscribeStorager和基于MSMQ的离线订阅存储实现EAS.Messages.MsmqSubscribeStorager。
EAS.Messages.DbSubscribeStorager:存储订阅关系在messageSubscribe.Config文件之中,消息存储在关系数据库SOA_SUBSCRIBEEVENTS表之中,使用前必须要建立相应的表结构,以下是SQL Server的DDL脚本:
1: CREATE TABLE [SOA_SUBSCRIBEEVENT](
2: [GUID] [varchar](36) NOT NULL,
3: [SUBSCRIBER] [nvarchar](128) NOT NULL,
4: [TOPIC] [nvarchar](128) NOT NULL,
5: [BODY] [image] NULL,
6: [FCTIME] [datetime] NOT NULL,
7: CONSTRAINT [PK_SOA_SUBSCRIBEEVENT] PRIMARY KEY CLUSTERED
8: (
9: [GUID] ASC
10: )
11: )
目前理论上支持SQLServer 、Mysql、ORACLE、Sqlite四种数据库结构,具体建表脚本请自行参考相应资料书写,也可以使用AgileEAS.NET SOA中间件所提供的数据库初始化工具创建。
EAS.Messages.MsmqSubscribeStorager:存储订阅关系在messageSubscribe.Config文件之中,消息存储Msmq消息队列之中,使用之前请确保机器上安装了MSMQ消息对列。
五、关于自定义实现ISubscribeStorager
有兴趣的朋友可以自定义实现接口ISubscribeStorager,这样就可以按自己的规则进行存储,比如把离线消息存储到mongodb、Redis、或者直接存储在文件之中,或者其他更多的实现规则,在此就不一一介绍,如有相关兴趣,请联系作者,如确有必要需要给在家介绍一下如何实现,将会另开一文本介绍如何自定义实现ISubscribeStorager接口。
六、改进在线例子支持离线
还是跟上次一样,以案例为在家展示一下怎么进行离线消息,就不重新开始例子,对原有例子做一些改进,改进后例子如下:
其中在原有项目的基础上增加了:Demo.Subscriber1和Demo.Subscriber2项目,其项目配置代码、配置文件基本上同Demo.Subscriber一样,其中唯一的差别在于,Demo.Subscriber1和Demo.Subscriber2向服务器提交订阅的时候都增加一个另friendName参数,其使用IMessageBus接口的以下订阅函数:
1: /// <summary>
2: /// 订阅消息。
3: /// </summary>
4: /// <param name="subscriber">订阅者。</param>
5: /// <param name="friendName">订阅者名称,用于处理离线订阅。</param>
6: /// <param name="topic">主题。</param>
7: /// <param name="notifyHandler">订阅通知。</param>
8: void Subscribe(object subscriber,string friendName ,string topic, MessageNotifyHandler notifyHandler);
Demo.Publisher项目为发布者代码。
Demo.Subscriber项目为订阅者代码。
Demo.Server项目为服务端代码。
Demo.Subscriber1项目之中,其Program.cs代码如下:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Windows.Forms;
5: using EAS.Messages;
6:
7: namespace Demo.Subscriber1
8: {
9: class Program
10: {
11: static void Main(string[] args)
12: {
13: var container = EAS.Objects.ContainerBuilder.BuilderDefault();
14: var bus = container.GetComponentInstance("MessageBus") as IMessageBus;
15: System.Console.WriteLine("Subscriber1");
16:
17: bus.Subscribe(new Program(), "Subscriber1", Demo.Messages.Topics.DEMO_TOPIC, MessageNotify);
18: System.Console.ReadLine();
19: }
20:
21: static void MessageNotify(object m)
22: {
23: Demo.Messages.Message message = m as Demo.Messages.Message;
24: System.Console.WriteLine(string.Format("Subscribe:{0}", message.ID));
25: }
26: }
27: }
其中bus.Subscribe(new Program(), "Subscriber1", Demo.Messages.Topics.DEMO_TOPIC, MessageNotify);在订阅消息的时候给了一个friendName为Subscriber1,Demo.Subscriber2与Demo.Subscriber1项目的唯一的差别就是此处为Subscriber2.
我们使用内置的EAS.Messages.DbSubscribeStorager,则不需要修改服务端的代码,只需要修改服务端的配置文件如下:
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <configSections>
4: <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel" />
5: </configSections>
6: <startup useLegacyV2RuntimeActivationPolicy="true">
7: <supportedRuntime version="v4.0"/>
8: </startup>
9: <eas>
10: <objects>
11: <!--数据库连接-->
12: <object name="DbProvider" assembly="EAS.Data" type="EAS.Data.Access.SqlClientDbProvider" LifestyleType="Thread">
13: <property name="ConnectionString" type="string" value="Data Source=.;Initial Catalog=eas_db;Integrated Security=SSPI;Connect Timeout=30" />
14: </object>
15: <!--数据访问器-->
16: <object name="DataAccessor" assembly="EAS.Data" type="EAS.Data.Access.DataAccessor" LifestyleType="Thread">
17: <property name="DbProvider" type="object" value="DbProvider"/>
18: <property name="Language" type="object" value="TSqlLanguage"/>
19: </object>
20: <!--ORM访问器-->
21: <object name="OrmAccessor" assembly="EAS.Data" type="EAS.Data.ORM.OrmAccessor" LifestyleType="Thread">
22: <property name="DataAccessor" type="object" value="DataAccessor"/>
23: </object>
24: <!--查询语言-->
25: <object name="TSqlLanguage" assembly="EAS.Data" type="EAS.Data.Linq.TSqlLanguage" LifestyleType="Thread"/>
26: <!--消息持久化-->
27: <object name="SubscribeStorager" assembly="EAS.SOA.BootStrap" type="EAS.Messages.DbSubscribeStorager" LifestyleType="Singleton"/>
28: <!--日志管理-->
29: <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton">
30: <property name="RootPath" type="string" value="G:\App.Work\Pub_Sub\Offline\Publish\logs" />
31: </object>
32: </objects>
33: </eas>
34: </configuration>
在配置文件的IOC配置之中我们配置了消息存储对象以及其所依赖的数据库访问对象、Linq查询语言表达式,另外需要说明的是,我们需要把配置文件之中所涉及的EAS.Data.dll、EAS.SOA.BootStrap.dll复制到编译输出Publish,这两个文件可以从AgileEAS.NET SOA 中间件平台发布包之中寻找,本案例的下载压碎包之中会包括这两个文件。
有关于基于Msmq的配置,只需要修改配置文件如下:
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <configSections>
4: <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel" />
5: </configSections>
6: <startup useLegacyV2RuntimeActivationPolicy="true">
7: <supportedRuntime version="v4.0"/>
8: </startup>
9: <eas>
10: <objects>
11: <!--消息持久化-->
12: <object name="SubscribeStorager" assembly="EAS.SOA.BootStrap" type="EAS.Messages.MsmqSubscribeStorager" LifestyleType="Singleton"/>
13: <!--日志管理-->
14: <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton">
15: <property name="RootPath" type="string" value="G:\App.Work\Pub_Sub\Offline\Publish\logs" />
16: </object>
17: </objects>
18: </eas>
19: </configuration>
到此为止,所有代码均已完成,是不是很简单,接下来,我们跑起来验证一下效果。
七、验证效果
我们在编译输入目录Publish下先启动Demo.Server.exe,再各启动Demo.Subscriber.exe、Demo.Subscriber1.exe、Demo.Subscriber2.exe,再启动一个Demo.Publisher.exe,在Demo.Publisher.exe控制台按回车键:
目前程序三个订阅者都是在线的,Demo.Publisher发布了三条消息,三个订阅者都收到了三条消息,那么我们关闭Demo.Subscriber2之后再由Demo.Publisher发布两条消息:
然后我们再启动Demo.Subscriber2,看是否还能收到其离线之后由Demo.Publisher发布的两条消息:
OK,到此为止。
八、源代码下载
本程序的源代码已上传到服务器,请通过http://112.74.66.105/downloads/eas/Demo.Pub_Sub_Offline.rar进行下载,如果在开发过程之中想要了解更多有关Socket通信框架以及更多AgileEAS.NET SOA中间件平台的技术资源,请通过AgileEAS.NET SOA 网站:http://www.smarteas.net的最新下载栏目进行下载。
九、问题反馈
麻烦大家在通过视频进行学习的时候能及时把问题反馈给楼主,或者有什么需要改进的一些建议都请向楼主直接反馈,以下是联系方式:
AgileEAS.NET SOA 网站:http://www.smarteas.net
官方博客:http://eastjade.cnblogs.com
楼主QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
120661978(AgileEAS.NET 平台交流)/上限1000人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
by 魏琼东
2014.4.25 11:20
接触AgileEAS.NET SOA 中间件平台(以下简称EAS.NET平台)有4个多月时间,经过试用认为可以把它作为一个开发的基础平台,开发团队可以把开发的重点放在需求的把控和项目的交付上,从而节省大量的时间,提高项目的开发、交付效率,降低对项目团队的深层技术要求,更重要的一点是EAS.NET平台的开发团队持续不断地维护和改进平台以及对反馈问题的快速反应,使我对平台的持续发展充满信心。
由于EAS.NET平台的资料比较多,需要花费较多的时间才能够初步了解平台并能够使用平台开始开发,所以,在此,我把学习EAS.NET平台的过程总结了一下,形成本篇短文,希望能够让初次接触EAS.NET平台的朋友能够用30分钟时间,跟着本文案例实际操作一遍,对EAS.NET平台有一个真实的体验,节省大家的时间,也算对EAS.NET平台的一点回报,希望有更多的人了解它的优势并真正用好它,为EAS.NET平台的使用者带来价值,也为EAS.NET平台的开发者带来效益,最终实现合作共赢的美好结局。
[More]
by 魏琼东
2013.12.19 21:55
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、AgileEAS.NET SOA中间件的分布式结构 AgileEAS.NET SOA中间件平台吸吸引人的一个特色就是其分布式架构体系,其可以基于高层的WCF协议和底层的.NET Socket通信建设统一一致的分布式通信服务,如下图所示的分布式结构: 系统中所有的客户端业务处理请求都经由AppServer处理之后返回结果给客户端,这个一个极其经典的SOA架构的分布式结构。 当系统的业务运行量大到一台AppServer无法满足情况下,那么我们就要考虑纵向扩展或者横向扩展以满足系统性能和业务响应的问题,最常用的选择是横向扩展,使用负载均衡机制以确保业务请求由多台AppServer完成;以及对系统的可靠性要求很高的时候,因为只有一台AppServer如果AppServer宕机了,那么整个系统都无法运行。我们就必须的考虑整个系统的故障切换能力,以确定系统的高可用性,系统的架构就会衍生为以下结构: 可以对比发布,前面架构图之中的AppServer已经由一台NlbServer和多台AppServer组成的服务器组合替换,系统之中不再存在当AppServer宕机之后系统无法服务的问题,也解决了随着业务量增加动态扩容的问题。 三、AgileEAS.NET SOA平台服务监控工具 为了方便系统维护人员,我们为AgileEAS.NET SOA中间件平台应用程序服务AppServer和负载均衡服务NlbServer提供了强大的可视化远程监控工具,让系统维护人员可以方便的通过远程监控工作进行监控。 应用程序服务监控工具为EAS.SOA.Monitor.exe应用程序: 其主要提供以下监控参数: 服务器资源监控:服务器CPU、内存、磁盘读取、网络通信等重要的服务器资料的实时监控及历史监控曲线图,如上图。 Socket服务监控:监控AppServer的Socket通信服务参数的各种状态,包括并发连接数、数据发送和接收速度: WCF服务监控:监控AppServer的WCF通信服务参数的各种状态,包括实时服务调用、最大并发、服务实例、累计服务调用次数等: 应用服务监控,监控AgileEAS.NET SOA服务的并发调用和累计调用: 负载均衡服务监控工具为EAS.NLB.Monitor.exe应用程序: 其提供的监控功能和监控参数同应用程序服务监控工作提供的类型,都提供了服务器资源监控、WCF服务监控、Socket通信监控,不一样的是负载均衡服务提供了对负载简单应用程序服务器的状态监控: 四、如何操作服务监控 不管是应用程序服务监控还是负载均衡服务监控,启动监控程序之前都需要使用监控客户端连接到用程序服务或者负载均衡服务。 初始的监控程序需要通过服务连接登录界面新建一个服务监控连接,弹出新建连接对话框: 监控连接参数主要由服务器的IP地址及所开放的端口决定,WCF或者Socket,应用程序服务监控工作提供WCF和Socket的连接,而负载均衡服务监控仅提供WCF形式的连接。 当创建好监控连接之后,下一次使用的时候就不需要再次配置监控连接信息了,只需要在服务连接下拉列表框中选择合适的的连接信息即可: 我们计划为监控程序提供一个动态的连接密钥或者证书验证机制,以便阻止非授权的监控连接请求,但是目前还没有启动连接密钥或者安全证书,在稍后的不久我们将会为大家提供这方面的功能。 五、联系我们 为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。 AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。 我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。 团队网站:http://www.agilelab.cn, AgileEAS.NET网站:http://www.smarteas.net 官方博客:http://eastjade.cnblogs.com QQ:47920381,AgileEAS.NET QQ群:113723486(AgileEAS SOA 平台)/上限1000人 199463175(AgileEAS SOA 交流)/上限1000人 212867943(AgileEAS.NET研究)/上限500人 147168308(AgileEAS.NET应用)/上限500人 172060626(深度AgileEAS.NET平台)/上限500人 116773358(AgileEAS.NET 平台)/上限500人 125643764(AgileEAS.NET探讨)/上限500人 193486983(AgileEAS.NET 平台)/上限500人 邮件:james@agilelab.cn,mail.james@qq.com, 电话:18629261335。
by 魏琼东
2013.12.19 18:59
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、关于AgileEAS.NET SOA中间件的分布式结构 AgileEAS.NET SOA中间件平台吸吸引人的一个特色就是其分布式架构体系,其可以基于高层的WCF协议和底层的.NET Socket通信建设统一一致的分布式通信服务,如下图所示的分布式结构: 系统中所有的客户端业务处理请求都经由AppServer处理之后返回结果给客户端,这个一个极其经典的SOA架构的分布式结构。 当系统的业务运行量大到一台AppServer无法满足情况下,那么我们就要考虑纵向扩展或者横向扩展以满足系统性能和业务响应的问题,最常用的选择是横向扩展,使用负载均衡机制以确保业务请求由多台AppServer完成;以及对系统的可靠性要求很高的时候,因为只有一台AppServer如果AppServer宕机了,那么整个系统都无法运行。我们就必须的考虑整个系统的故障切换能力,以确定系统的高可用性,系统的架构就会衍生为以下结构: 可以对比发布,前面架构图之中的AppServer已经由一台NlbServer和多台AppServer组成的服务器组合替换,系统之中不再存在当AppServer宕机之后系统无法服务的问题,也解决了随着业务量增加动态扩容的问题。 三、AgileEAS.NET SOA平台NLB支持 AgileEAS.NET SOA中间件平台的AppServer支持程序为EAS.SOA.Server.exe或EAS.SOA.Server.x64.exe,其中EAS.SOA.Server.exe为32位服务程序,EAS.SOA.Server.x64.exe为64位服务程序。 AppServer能在同一个进程之内同时支持Socket通信和WCF通信,即能同样的业务同步提供Socket和WCF通信方式,WCF通信提供http和net.tcp通道的访问,客户端应用使用何种通信方式由客户段程序决定。 AgileEAS.NET SOA中间件平台的在5.0版本之后增加了一个应用负载均衡服务程序EAS.NLB.Server,也同样提供了32位与64位的服务应用程序EAS.NLB.Server.exe或EAS.NLB.Server.x64.exe。 在AgileEAS.NET SOA中间件平台5.0版本之中为AppServer、NLB.Server提供了运行监控程序,NLB.Server的服务监控程序同时提供了NLB.Server的配置管理功能: 负载节点监控,会监控NLBServer后挂接的所有AppServer清单以及各个AppServer服务器的工作状态,是在线还是离线,在线的则能向客户端提供服务,如果离线了则表示不能向客户端提供服务了,在NLBServer负载过程之中,只会在在线的AppServr之间进行负载。 四、AgileEAS.NET NLBServer配置案例 在第三节我们已经对NLB做过一些简单的介绍,接下来我们以DrugShop案例环境为基础实例演示一下如何在应用环境之中启用NLBServer支持: 首先我们简单规划一下测试环境: 接下为我们一一配置,首先,我们参考AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(二):配置WinClient分布式运行环境一文配置好一个AppServer实例AppServer1,端口号设置Socket:6706,Wcf-tcp:6707,Wcf-http:6708: 1: <appserver>
2: <channel>
3: <wcf enable="true">
4: <config tcpPort="6707" httpPort="6708"/>
5: <serviceThrottling maxConcurrentCalls="128" maxConcurrentInstances="128" maxConcurrentSessions="256"/>
6: <wcfServices>
7: <wcfService key="Key" type="TValue"/>
8: </wcfServices>
9: </wcf>
10: <socket enable ="true">
11: <config tcpPort="6706"/>
12: <serviceThrottling maxConcurrence="1024"/>
13: <socketServices>
14: <socketService key="IMSocketService" type="AgileIM.Service.IMSocketService,AgileIM.Service"/>
15: </socketServices>
16: </socket>
17: </channel>
18: <appServices>
19: <appService key="Key" type="TValue"/>
20: </appServices>
21: </appserver>
然后复制配置好的AppServer目录到其他地方建立AppSerer2,端口信息修改为Socket:6706,Wcf-tcp:6707,Wcf-http:6708:
1: <channel>
2: <wcf enable="true">
3: <config tcpPort="6807" httpPort="6808"/>
4: <serviceThrottling maxConcurrentCalls="128" maxConcurrentInstances="128" maxConcurrentSessions="256"/>
5: <wcfServices>
6: <wcfService key="Key" type="TValue"/>
7: </wcfServices>
8: </wcf>
9: <socket enable ="true">
10: <config tcpPort="6806"/>
11: <serviceThrottling maxConcurrence="1024"/>
12: <socketServices>
13: <socketService key="IMSocketService" type="AgileIM.Service.IMSocketService,AgileIM.Service"/>
14: </socketServices>
15: </socket>
16: </channel>
17: <appServices>
18: <appService key="Key" type="TValue"/>
19: </appServices>
20: appserver>
同样复制AppServer目录配置NlbServer,NlbServer服务目前没有配置工具,只能自己修改配置文件EAS.NLB.Server.exe.config或者EAS.NLB.Server.x64.exe.config:
1: <?xml version="1.0"?>
2: <configuration>
3: <configSections>
4: <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel"/>
5: </configSections>
6: <!--支持混合程序集-->
7: <startup useLegacyV2RuntimeActivationPolicy="true">
8: <supportedRuntime version="v4.0"/>
9: </startup>
10: <eas>
11: <configurations>
12: <item name="Key" value="Value"/>
13: </configurations>
14: <nlbserver>
15: <config>
16: <keepAlive timeSpan="6" chnanel="WCF"/>
17: </config>
18: <channel>
19: <wcf enable="true">
20: <config tcpPort="6607" httpPort="6608"/>
21: <serviceThrottling maxConcurrentCalls="128" maxConcurrentInstances="128" maxConcurrentSessions="256"/>
22: <wcfServices>
23: <wcfService key="Key" type="TValue"/>
24: </wcfServices>
25: </wcf>
26: <socket enable ="true">
27: <config tcpPort="6606" gatewayStyle="NetPacket"/>
28: <serviceThrottling maxConcurrence="10000"/>
29: </socket>
30: </channel>
31: </nlbserver>
32: <objects>
33: <!--NLB日志记录-->
34: <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton">
35: <property name="Path" type="string" value="..\logs" />
36: </object>
37: </objects>
38: </eas>
39: <startup>
40: <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
41: </startup>
42: </configuration>
配置其端口号为6606,Wcf-tcp:6607,Wcf-http:6608,配置完成后参考AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(二):配置WinClient分布式运行环境一文编写AppServer服务的安装、卸载和控制台启动脚本。
NLBServer的安装卸载和控制台启动脚本类似,都是由EAS.NLB.Server.exe -参数组成,只不过,EAS.NLB.Server只能支持在一台服务器安装一个实例,即同一台服务不充许像AppServer一样安装多个实现,以下是脚本示例:
1: @rem 安装NLB服务
2: EAS.NLB.Server.x64.exe -i
3:
4: @rem 卸载NLB服务
5: EAS.NLB.Server.x64.exe -u
6:
7: @rem 控制台启动NLB服务
8: EAS.NLB.Server.x64.exe -run
9: pause
OK,我们启动NLBServer和AppServer1、AppServer2:
接下来我们在客户端文件夹之中启动“负载均衡监控”程序EAS.NLB.Monitor.exe配置负载均衡服务的负载节点:
在服务连接之中选择新建连接,弹出新建连接对话框:
修改连接名为nlbServer,服务器为127.0.0.1,即连接本机nlbserver,实际情况之中请根据实际情况配置,端口号填入前面配置的NlbServer端口参数,我们此处选择以wcf-tcp连接,端口配置为6607,然后确定保存配置,并且进入“负载均衡监控”主界面:
“负载均衡监控”第一个界面为负载均衡服务器的CPU、内存、网络、磁盘等各种资源参数,供管理人员参考,我们切换到负载节点监控:
在空白区域右键打开快捷菜单点=>"添加节点":
根据AppServer参数添加负载节点信息,确定后保存负载节点信息,使用同样的方法添加AppSever2节点,完成后如下图:
也可以选择直接修改配置配置负载节点信息,负载节点信息存储在NlbServer工作目录中的“NLB.config”文件:
1: <?xml version="1.0" encoding="utf-16"?>
2: <NLB xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3: <items>
4: <NlbWorkItem>
5: <host>127.0.0.1</host>
6: <wcfChannel>
7: <HttpPort>6708</HttpPort>
8: <TcpPort>6707</TcpPort>
9: </wcfChannel>
10: <socketChannel>
11: <TcpPort>6706</TcpPort>
12: </socketChannel>
13: <enabled>true</enabled>
14: <runing>true</runing>
15: </NlbWorkItem>
16: <NlbWorkItem>
17: <host>127.0.0.1</host>
18: <wcfChannel>
19: <HttpPort>6808</HttpPort>
20: <TcpPort>6807</TcpPort>
21: </wcfChannel>
22: <socketChannel>
23: <TcpPort>6806</TcpPort>
24: </socketChannel>
25: <enabled>true</enabled>
26: <runing>false</runing>
27: </NlbWorkItem>
28: </items>
29: </NLB>
OK,现在已经完成了对负载均衡的配置,接下来我们配置客户端程序“DrugShop.Main.exe”的连接信息,可以选择由配置工具生成,也可以选择手工书写,定义为SOA分布式环境下wcf-tcp连接,连接端口为127.0.0.1:6607(NLBServer),配置文件如下:
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <configSections>
4: <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel" />
5: </configSections>
6: <!--SQLite运行必需-->
7: <startup useLegacyV2RuntimeActivationPolicy="true">
8: <supportedRuntime version="v4.0"/>
9: </startup>
10: <eas>
11: <configurations>
12: <item name="Key" value="Value" />
13: </configurations>
14: <objects>
15: <!--分布式通信组件。-->
16: <object name="ServiceBridger" assembly="EAS.Distributed" type="EAS.Distributed.ServiceBridger" LifestyleType="Thread" >
17: <property name="ServiceName" type="string" value="EAS.RMI.Service" />
18: </object>
19: <object name="DataAccessor" assembly="EAS.Distributed" type="EAS.Distributed.DataAccessor" LifestyleType="Thread" >
20: <property name="ServiceBridger" type="object" value="ServiceBridger" />
21: </object>
22: <object name="OrmAccessor" assembly="EAS.Distributed" type="EAS.Distributed.OrmAccessor" LifestyleType="Thread" >
23: <property name="ServiceBridger" type="object" value="ServiceBridger" />
24: <property name="DataAccessor" type="object" value="DataAccessor" />
25: </object>
26: <!--日志管理-->
27: <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton" />
28: <!--资源-->
29: <object name="EAS.Explorer.Resource" assembly="DrugShop.Res" type="DrugShop.Res.Resources" LifestyleType="Singleton" />
30: </objects>
31: <services>
32: <service name="EAS.RMI.Service" service-type="WcfService" singleton="true" url="net.tcp://127.0.0.1:6607/eas/services/EAS.RMIService" />
33: </services>
34: </eas>
35: </configuration>
OK,客户端程序配置完成。
五、测试验证
启动DrugShop.Main.exe,可以选择启动多个客户端程序实例,可以看到以控制台运行的两个服务实例都有不同程序的请求响应:
我们可以看到,客户端的业务请求被NLBServer进行了有效的负载,当我们随便停止其他任务一个AppServer,我们可以通过“负载均衡监控”程序看到其处于离线状态:
在实际项目之中可以是某一台AppServer宕机了或者人为的进行某种维护升级活动,在这个时间,所有的客户端程序的所有业务操作都是可以的,从这一点我们可以看出,AppServer被NLBServer进行了有效的故障转移,有效的提高了系统的高可用性。
如果所有的AppServer都离线了,即所有AppServer都宕机了,如下图:
那么这个时间,系统就不能提供任务的服务了,当我们在客户端执行任何操作都会报错:
当我们恢复负载之中的任务一个节点之后,系统则可以向使用者提供全部的业务服务。
六、联系我们
为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。
AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。
我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。
团队网站:http://www.agilelab.cn,
AgileEAS.NET网站:http://www.smarteas.net
官方博客:http://eastjade.cnblogs.com
QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET应用)/上限500人
172060626(深度AgileEAS.NET平台)/上限500人
116773358(AgileEAS.NET 平台)/上限500人
125643764(AgileEAS.NET探讨)/上限500人
193486983(AgileEAS.NET 平台)/上限500人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
by 魏琼东
2013.12.19 11:38
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、关于Lua语言 Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。 Lua语言目前应用最广泛的领域是游戏编程领域,最早接触和认识Lua也是因为在2008年玩金山的剑网3,通过解包读过其中的一些游戏脚本,慢慢也看过一些其他游戏的Lua脚本,在我们开发自己的电子病历系统的过程之中,引入了Lua脚本语言实现那些可变性非常高的场景。 .NET通过LuaInterface开源项目类库实现对Lua的脚本调用以及Lua与C#的相互绑定,有关于这此方面的内容请大家搜索相关文章以获得帮助。 在AgileEAS.NET SOA5.0版本之中,我们决定把在开发过程之中形成的Lua脚本引擎一并集成入AgileEAS.NET SOA中间件平台,目前Lua脚本语言被我们广泛的应用电子病历系统这中的病案自动评分、短信系统之中的交互式应答、和一些计划任系统之中的计划任务定义。 三、AgileEAS.NET SOA平台Lua引擎 AgileEAS.NET SOA5.0平台目前使用的是Lua5.1版本,使用LuaInterface实现C#与Lua的相互绑定处理,平台已经对其进行了二次封装以提供统一的API支持,目前由EAS.LuaScript.dll程序集承载所有业务。 AgileEAS.NET SOA平台Lua引擎提供了以下接口或类型的API: ILuaEngine,定义为一个Lua脚本引擎: 1: using System;
2:
3: namespace EAS.LuaScript
4: {
5: /// <summary>
6: /// Lua脚本解析引擎。
7: /// </summary>
8: /// <remarks>
9: /// 用于完成程序之中嵌入的动态Lua脚本,Lua脚本在AgileEAS.NET SOA 中间件之中主要用于环境的各种参数的动态解析处理之中。
10: /// </remarks>
11: public interface ILuaEngine:IDisposable
12: {
13: /// <summary>
14: /// 脚本路径。
15: /// </summary>
16: string ScriptDirectory
17: {
18: get;
19: set;
20: }
21:
22: /// <summary>
23: /// 输出重定向方法/用于调试。
24: /// </summary>
25: Action<object> OutAction
26: {
27: get;
28: set;
29: }
30:
31: /// <summary>
32: /// 注册lua函数,实现Lua绑定。
33: /// </summary>
34: /// <param name="luaAPIClass">lua函数类</param>
35: void BindLuaFunctions(object luaAPIClass);
36:
37: /// <summary>
38: /// 执行lua脚本文件。
39: /// </summary>
40: /// <param name="luaFileName">脚本文件名。</param>
41: /// <returns>lua脚本执行结果。</returns>
42: object[] DoFile(string luaFileName);
43:
44: /// <summary>
45: /// 执行lua脚本文本。
46: /// </summary>
47: /// <param name="chunk">lua指令。</param>
48: /// <returns>lua脚本执行结果。</returns>
49: object[] DoString(string chunk);
50:
51: /// <summary>
52: /// 调用Lua函数。
53: /// </summary>
54: /// <param name="luaFunction">函数名称。</param>
55: /// <param name="args">调用参数。</param>
56: /// <returns>lua脚本执行结果。</returns>
57: object[] Invoke(string luaFunction, params object[] args);
58: }
59: }
其中脚本路径ScriptDirectory指示Lua脚本文件的存储位置,在DoFile执行脚本文过程之中如果传入为相关路径则可以自动在ScriptDirectory之中寻址,ScriptDirectory默认为当前程序目录。
DoFile:执行脚本文件并且返回执行结果。
DoString:执行脚本代码并且返回执行结果。
Invoke:执行指定的脚本函数。
LuaFunctionAttribute,Lua脚本函数特性:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace EAS.LuaScript
7: {
8: /// <summary>
9: /// Lua函数描述特性。
10: /// </summary>
11: /// <remarks>
12: /// 用于标记Lua接口方法。
13: /// </remarks>
14: [AttributeUsage(AttributeTargets.Method,
15: Inherited = false, AllowMultiple = false)]
16: [Serializable]
17: public class LuaFunctionAttribute : Attribute
18: {
19: /// <summary>
20: /// 初始化LuaFunctionAttribute对象实例。
21: /// </summary>
22: /// <param name="name">函数名称。</param>
23: public LuaFunctionAttribute(string name)
24: : this(name, string.Empty)
25: {
26:
27: }
28:
29: /// <summary>
30: /// 初始化LuaFunctionAttribute对象实例。
31: /// </summary>
32: /// <param name="name">函数名称。</param>
33: /// <param name="description">函数描述。</param>
34: public LuaFunctionAttribute(string name, string description)
35: : this(name, description,null)
36: {
37:
38: }
39:
40: /// <summary>
41: /// 初始化LuaFunctionAttribute对象实例。
42: /// </summary>
43: /// <param name="name">函数名称。</param>
44: /// <param name="description">函数描述。</param>
45: /// <param name="funcParams">参数说明。</param>
46: public LuaFunctionAttribute(string name, string description, params string[] funcParams)
47: {
48: this.Name = name;
49: this.Description = description;
50: this.Params = funcParams;
51: }
52:
53: /// <summary>
54: /// 函数名称。
55: /// </summary>
56: public string Name
57: {
58: get;
59: internal set;
60: }
61:
62: /// <summary>
63: /// 函数描述。
64: /// </summary>
65: public string Description
66: {
67: get;
68: internal set;
69: }
70:
71: /// <summary>
72: /// 参数说明。
73: /// </summary>
74: public string[] Params
75: {
76: get;
77: internal set;
78: }
79: }
80: }
用于C#向Lua暴露函数接口,实现C#方法与Lua脚本函数的绑定,也可以实现为把C#的方法暴露给Lua脚本调用,如以下代码:
1: /// <summary>
2: /// 执行指定的非查询命令。
3: /// </summary>
4: /// <param name="commandText">要执行的命令语句。该语句必须是标准的数据库语句。</param>
5: /// <returns>返回命令影响的行数。</returns>
6: [LuaFunction("ExecuteSql", "执行指定的非查询命令,返回执行所影响的行数。", "要执行的SQL语句")]
7: public int Execute(string commandText)
8: {
9: IDataAccessor da = this.GetComponent();
10: if (da != null)
11: {
12: return da.Execute(commandText);
13: }
14: else
15: {
16: return -1;
17: }
18: }
其目标早实现C#方法Execute与Lua脚本函数ExecuteSql之间的绑定,当在Lua教本之中调用ExecuteSql,即会执行Execute方法。
LuaFramework,Lua框架,为Lua脚本引用最重要的API:
1: #region 程序集 EAS.LuaScript.dll, v4.0.30319
2: // G:\Health.Work\AgileEMR4.0\Publish\EAS.LuaScript.dll
3: #endregion
4:
5: using System;
6:
7: namespace EAS.LuaScript
8: {
9: // 摘要:
10: // Lua脚本执行框架/上下文。
11: //
12: // 备注:
13: // 应用系统之中调用Lua脚本的入口。
14: public sealed class LuaFramework
15: {
16: // 摘要:
17: // LuaFramework对象的唯一实例。
18: public static LuaFramework Instance { get; }
19:
20: // 摘要:
21: // 注册lua函数,实现Lua绑定。
22: //
23: // 参数:
24: // luaAPIClass:
25: // lua函数类
26: public static void BindLuaFunctions(object luaAPIClass);
27: //
28: // 摘要:
29: // 从应用程序上下文环境之中获取指定名称的对象实例。
30: //
31: // 参数:
32: // componentKey:
33: // 组件名称。
34: //
35: // 返回结果:
36: // 对象实例。
37: public object GetComponent(string componentKey);
38: //
39: // 摘要:
40: // Lua脚本引擎。
41: public static ILuaEngine GetLuaEngine();
42: //
43: // 摘要:
44: // Lua脚本引擎。
45: public static ILuaEngine GetLuaEngine(string scriptDirectory);
46: //
47: // 摘要:
48: // 取系统时间。
49: [LuaFunction("GetTime", "取系统时间")]
50: public DateTime GetTime();
51: }
52: }
其中BindLuaFunctions方法实现C#方法与Lua函数的绑定注册,调用时传入需要注册方法所在的C#类对象实例即可。
GetLuaEngine获取一个ILuaEngine,为之后的执行脚本作准备,调用GetLuaEngine可选传入scriptDirectory参数,scriptDirectory为Lua脚本存储目录。
AgileEAS.NET SOA中间件已经为大家提供了少数的几个预定义API绑定:
1: helpcmd(cmdName) - Show help for a given command or package
2: help() - List available commands.
3: Include(luaFileName) - 预加载指定的脚本文件
4: echo(message) - 显示Lua调试信息,用于Lua的动态调试。
5: Echo(message) - 显示Lua调试信息,用于Lua的动态调试。
6: WriteLog(message) - 写日志记录,用于Lua脚本的动态调试。
7: GetTime() - 取系统时间
8: GetComponent(componentKey) - 从应用程序上下文环境之中获取指定名称的对象实例
9: GetAccount() - 求当前账户信息
10: ExecuteSql(commandText) - 执行指定的非查询命令,返回执行所影响的行数。
11: QuerySql(commandText, resultType) - 执行给定的数据库查询命令,返回执行结果,返回结果由resultType参数决定。
12: QuerySqlScalar(commandText) - 执行给定的数据库查询命令,仅返回第一行第一列结果
13: QuerySqlMatrix(commandText) - 执行给定的数据库查询命令,返回第一个查询结果Matrix
14: QuerySqlDictionary(commandText) - 执行给定的数据库查询命令,返回第一行的Key-Value
15: QuerySqlList(commandText) - 执行给定的数据库查询命令,返回第一行的Key-Value
16: QuerySqlDataSet(commandText) - 执行给定的数据库查询命令,返回查询结果集
17: QuerySqlDataTable(commandText) - 执行给定的数据库查询命令,返回查询结果集的第一个DataTable
18: GetDataSet() - 求当前系统的帐套信息
大家也可以基于AgileEAS.NET SOA 平台Lua引擎扩展自己的LuaAPI。
四、AgileEAS.NET SOA平台Lua引擎应用案例
在我12年的医疗行业开发之中,做过多年的电子病历,开发、指导开发过多套电子病历系统,在电子病历系统之中,有以下几个问题一直得不到好的解决方案:
在电子病历质控之中有一个自动质控评分,其由程序自动的预先为某个病案打出一个得分:
在没有使用Lua教本之前呢,也是能实现这种自动评分,但是为了实现这样的功能,系统设计之中就会要增加一些辅助的表和大量的辅助设计以帮助完成这样的功能,因为毕竟不存在统一的评分规则,所以这个设计会极其的复杂,以致于很难把这样的功能做好,或者说在成本和效益估量之中,得不偿失。
另外还有比较简单的例子就是电子病历之间的宏替换问题,在书写电子病历的过程之中需要动态的插入如病人基本信息、医院名称这样的东西进入病历,但是因为其数据源并不是唯一确定的,如果不使用动态脚本设计,那么其程序这中就会存在大量的硬编码,并且如果想要在后期实施阶段增加宏,那么则必须要修改程序才能完成。
下面我们就以自动评分案例向大家讲解一下是如何实现功能的呢:
首先,系统之中必须管理和维护用于自动评分的所有评分规则:
这里与其他很多开发者的做法不一样的是,对于每个缺陷项目,我们都为其提供了一个评分规则的脚本的设计,例如对于缺陷“*缺入院记录”的评分脚本如下:
1: -----------------------------------------------------------------------------------------------
2: -- 创建人 : 魏琼东
3: -- 创建时间 : 2013-09-05
4: -- 效果备注 : *缺入院记录评分脚本。
5: -- GetErrorNumber,求缺陷数量,由脚本计算出本缺陷数量
6: ----------------------------------------------------------------------------------------------
7: ------------------------不怎么华丽的分割线--------------------------------------
8: function GetErrorNumber(patient,errorItem)
9:
10: --调用C#,求监控项目
11: local monitorItem = GetMonitorItem("B01")
12: --监控表达式,无表达式返回0
13: local script = monitorItem.Expression
14: if script == nil then
15: return 0
16: elseif script == "" then
17: return 0
18: end
19:
20: --加载监控脚本
21: ExecuteScript(script)
22: --执行监控函数NeedWrite,如果需要写则预标存在此缺陷
23: local need = NeedWrite(patient,monitorItem)
24:
25: if need == 1 then
26: return 1
27: else
28: return 0
29: end
30:
31: end
这样的设计的目标是将缺陷的评分动作都由Lua脚本实现,而在应用程序之中并不存在针对缺陷制定不同的评分规则的情况,只有调用脚本进行评分的代码:
1: /// <summary>
2: /// 求指定患者、指定缺陷的缺陷数。
3: /// </summary>
4: /// <param name="pRoot"></param>
5: /// <param name="errorItem"></param>
6: /// <returns></returns>
7: public static int GetErrorNumber(ILuaEngine m_Lua, PatientRoot pRoot, ErrorDict errorItem)
8: {
9: if (string.IsNullOrEmpty(errorItem.Expression)) //无评分脚本
10: {
11: return 0;
12: }
13:
14: string m_file = System.IO.Path.Combine(LuaEx.ScriptDirectory, string.Format("emr\\error\\{0}.lua", errorItem.ErrorCode));
15: if (!System.IO.File.Exists(m_file))
16: {
17: return 0;
18: }
19:
20: try
21: {
22: m_Lua.DoFile(m_file); //加载脚本
23:
24: object[] vs = m_Lua.Invoke("GetErrorNumber", pRoot, errorItem); //执行函数。
25: if (vs != null && vs.Length > 0)
26: {
27: return int.Parse(vs[0].ToString());
28: }
29: else
30: {
31: return 0;
32: }
33: }
34: catch
35: {
36: return 0;
37: }
38: }
这样的好处在于,主体程序变的极其的简单一致,不需要各种判断和各种规则,并且灵活自动,可以做到政策变化或者客户需求变化导到的各种评分规则的修改,则会灵活自如。
实践证明,使用动态脚本语言扩展现在系统的系统是一种低投入高产出的工作,对于应对这种高扩展性和高定制性的项目是一个非常好的选择,这个可以解释游戏编程之中大量使用Lua语言的事实,针对NPC、剧情书写相关的处理脚本,让游戏的后期变更,调整变得简单可行。
四、联系我们
为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。
AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。
我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。
团队网站:http://www.agilelab.cn,
AgileEAS.NET网站:http://www.smarteas.net
官方博客:http://eastjade.cnblogs.com
QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET应用)/上限500人
172060626(深度AgileEAS.NET平台)/上限500人
116773358(AgileEAS.NET 平台)/上限500人
125643764(AgileEAS.NET探讨)/上限500人
193486983(AgileEAS.NET 平台)/上限500人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
by 魏琼东
2013.12.18 22:29
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、关于按钮级权限 在AgileEAS.NET SOA5.0版本之中,AgileEAS.NET SOA平台仅提供了基于模块的权限管理体系,也没有提供按钮/子功能级别的权限控制体系。 有很多很多的使用者基于AgileEAS.NET SOA 中间件平台进行应用开发的过程之中都提到这个问题,我们也给大家提供了一些解决思路和方案,但是一直没有在平台之中集成和实现一套标准的按钮/子功能权限管理功能和标准的API。 在AgileEAS.NET SOA5.0版本发布之前我们实现一套按钮/子功能级别的权限控制体系,提供了一个用于实现按钮级权限管理功能的管理模块及一给API接口。 三、按钮级权限管理 在AgileEAS.NET SOA5.0版本之中,AgileEAS.NET SOA平台仅提供了基于模块的权限管理体系,也没有提供按钮/子功能级别的权限控制体系。 使用管理员登录AgileEAS.NET SOA平台WinClient容器,就会在系统管理导航组之中看到有一个“函数管理”模块: 列表之中是所有的按钮权限项目,存储在模块信息表之中,如果需要增加一个新的“按钮/函数/权限项目”,使用添加功能,打开添加界面: 以上界面的操作会增加一个名称为“药品字典打印”字的按钮级别的权限,其权限ID为“6a185217-34de-4fe7-9ed5-c2cc49ba432c”。 双击选中的按钮级别的权限项,会打开“函数属性”对话框可以针对此函数/权限项进行授权: 授权方式同模块级权限一模一样,你也可以通过账号管理和角色管理模块进行授权。 四、API及如何使用 光提供一个管理功能还是不够的,还必须要提供一组API以供开发人员使用,AgileEAS.NET SOA中间件平台在应用程序对象(EAS.Application类的单例对象EAS.Application.Instance)上定义一个验证权限的API: 1: //
2: // 摘要:
3: // 验证权限,验证当前系统登录人员是否具有某个构件的访问权限。
4: //
5: // 参数:
6: // component:
7: // 构件Guid。
8: //
9: // 返回结果:
10: // 验证通过返回True,否则返回False。
11: bool Demand(Guid component);
Demand方法需要传入按钮级别权限项/函数的定义ID,其他由交由平台完成。
接下来我们在DrugShop案例案例之中的药品字典模块之中增加对“打印”按钮的权限控制,Visual Studio打开DrugShop解决方案打开“药品字典”模块插件的代码文件DrugDictList.cs,修改打印按钮的事件如下:
1: private void tsbPrint_Click(object sender, EventArgs e)
2: {
3: //验证按钮级别权限。
4: if (!EAS.Application.Instance.Demand(new Guid("6a185217-34de-4fe7-9ed5-c2cc49ba432c")))
5: return;
6:
7: if (this.dictList != null)
8: {
9: EAS.GReport.Controls.PrintViewDialog ViewDialog = new EAS.GReport.Controls.PrintViewDialog();
10: ViewDialog.ReportID = new Guid("F29C4C91-0791-4116-BE26-3A2A88F30A2A");
11: ViewDialog.DataObject = this.dictList;
12: ViewDialog.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
13: ViewDialog.WindowState = System.Windows.Forms.FormWindowState.Maximized;
14: ViewDialog.ShowDialog();
15: }
16: }
这样就完成了对本权限项的调用验证。
四、执行验证
我们编译并且启动DrugShop.Main.exe,使用0001密码sa登录,打开“药品字典”模块,查询数据之后点击“打印”按钮:
0001不具有这个按钮级权限项的权限,所以会提供没有权限,我们换0002登录并且执行同样的操作:
OK,可以正常使用了,按钮级权限演示完成。
DrugShop案例之中同步提供了本例子的权限定义和调用代码,请大家通过AgileEAS.NET SOA中间件官方网站的最新下载栏目进行下载。
五、联系我们
为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。
AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。
我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。
团队网站:http://www.agilelab.cn,
AgileEAS.NET网站:http://www.smarteas.net
官方博客:http://eastjade.cnblogs.com
QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET应用)/上限500人
172060626(深度AgileEAS.NET平台)/上限500人
116773358(AgileEAS.NET 平台)/上限500人
125643764(AgileEAS.NET探讨)/上限500人
193486983(AgileEAS.NET 平台)/上限500人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
by 魏琼东
2013.12.18 20:23
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、关于动态报表系统 在之前的犀利的报表系统,AgileEAS.NET SOA中间件平台GReport使用指南一文之中我们为大家介绍和实例演示了如何使用AgleEAS.NET SOA中间件平台的GReport报表系统。 但是,在之前提供的报表系统之中,不管是fyireporting报表系统还是GReport报表系统,都是预定义报表系统,即需要在程序开发过程之中进行预定义,虽然留给项目实施人员和维护人员极大的自定义功能,但是必竟是有所限制的,比如不能修改报表的ReportID,不能变更报表依赖的ORM实体对象。 而在现实项目之中,特别是偏重于数据查询、统计分析的MIS类系统,项目后期需要大量的统计分析报表,而在项目开发过程之中,毕竟不可能把所有的报表都开发出来,因为其需求并不是在开发阶段所提供的,而是在系统运行过程之中,随着对系统应用的深入会有越来越多的报表需求,那么在这个时间,再由开发人员重新定义报表,显然有些不能接受,一是项目的成果会明细的提高,二是因为开发人员的流动和其他一些因素,比如开发企业的售后服务响应周期较长,不能及时的响应这种需求。 为解决此问题,AgileEAS.NET SOA中是件平台创新的提出了动态报表的思路,即我们系统的报表进行了大致的划分,开发前提能明确确定的业务票据和业务报表由开发人员在开发过程之中实现,而对于因为不同的企业、用户的不同管理需求而导致变化的那些可变化报表系统,则交由项目实施人员或者系统应用者的维护人员进行实现。 这样做,可以为开发企业和系统使用者都能提出更大的便利,对于开发企业来讲,把一部分报表后置交由实施人员根据客户的具体情况在项目实施周期之内完成,一是可以节省前提开发成本,减少返工带来的高成本,二是可以把产品做成稳定的基础部分+动态定义的报表系统,便于对软件系统的定型和维护,而对于系统使用者也是也极大的便利,比如一些急需的或者特别的报表系统,则可以直接由自己的实施人员搞定,并且随便可以增加和修改,减少需要的实现周期。 AgileEAS.NET SOA中间件平台的动态报表系统其本质是定义一个依赖于一个动态SQL查询的数据报表以及对这个报表的模块转换发布两个功能实现。 AgileEAS.NET SOA中间件平台没有为“动态报表”提供单独的定义和管理功能,其管理功能集成于“GR报表”和“报表管理”两个模块之中。 和犀利的报表系统,AgileEAS.NET SOA中间件平台GReport使用指南一文之中定义普通报表不一样的地方是,动态报表在定义过程之中必须要选中“报表模块、把报表做为独立模块发布”复选框,并且必须要填写报表的“查询语句”,如果“查询语句”是一个带参数的动态查询语言,则必须要定义“查询参数”。 以下对几个重要参数做一下说明: “报表模块、把报表做为独立模块发布”:动态报表必须要中此项,选中本选项之后,系统会自动把这个报表发布为一个“报表模块”,即让报表充当系统之中的模块插件,一个报表即为一个模块插件,可以对于进行权限和导航部署,系统之中对此报表具体权限的账号登录系统之中就会以普通模块一样的方式打开报表并且使用。 “查询语句”:动态报表的查询语句是报表工作过程之中抓取报表数据的动态查询语句,报表的绑定数据来自于本查询语句与查询参数动态组合而成的目标查询结果: 查询语句之中以$符号起始的单词即为动态查询参数,此查询条件会根据最终用户的输入进行替换,如果查询语句之中定义了动态查动态查询参数,则必须定义报表的“查询参数”。 “查询参数”:查询参数用于详细定义解释报表动态查询语句之中的动态参数,比如参数的显示标题、参数数据类型、参数默认值,参数是否与某输入字典相关联等,下图的查询参数与上图你动态查询语句相匹配: 数据类型,目前支持“字符”、“日期”、“日期时间”、“整数”、“小数”。 查询参数被用于在打开报表自动生成的查询UI界面上输入预值的参数值,以便于动态生成报表数据。 三、动态报表实践 开发动态报表不需要进行C#或者VB.NET编程,但是需要掌握系统的数据库结构、会设计、制作报表和熟练的使用AgileEAS.NET SOA中间件平台的系统管理功能,符合这种条件的人员主要是系统实施人员和系统维护人员。 下面我们就是GReport动态报表一步一步的学习和演示一下处理过程。 首先,我们需要打开平台的“GR报表”模块,并且增加一个新的报表: 必须选中“报表模块、把报表做为独立模块发布”,然后切换到“查询语句”Tab页并且写入如下SQL语句: 这个SQL语句之中带了两个动态参数:$StartTime和$EndTime两个参数,我们的目标是统计LMTIME在这两个参数区间的模块,并且以程序集为分组汇总条件。 然后我们切换到“查询语句”Tab页,我们可以看到: 系统自动的从“查询语句”之中提取StartTime和EndTime,但是显示标题、数据类型、迷人值还需要我们手动修改和填写,填写完成如下图: 这样就完成了“查询参数”的定义,点“确定”按钮这后保存报表。 接下来双击刚添加的动态报表,系统会打开设计界面,自动加载初始样式: 请根据情况自己修改报表,最后保存,我们修改为以下样式: 这样报表的设计工作就完成了,接下来我们为动态报表进行使用者授权和导航分组部署,首先我们打开“角色管理”并且选中一个角色(我们选择“平台演示”角色),然后双击打开: 双击“添加”按钮,打开“模块选择界面”: 选择“搜索报表”复选框,输入检索条件,点“搜索”按钮之后选中刚才加载的模块,点击确定,返回“角色”属性设定界面。 点击“确定”完成对动态报表的访问授权。 接下来我们设置导航分组的部署,打开“导航分组”选择要部署动态报表的分组,如果没有分组请添加一个新的分组,有关于如何添加 一个新的导航分组的使用说明请参考《AgileEAS.NET SOA中间件系统管理使用教程 V5.0》。 选中一个分组,然后后右边空白区域右键打开快捷菜单,选择“添加”打开“模块选择窗口”: 选择动态报表然后单击“确定”按钮,完成对动态报表的导航分组部署。 四、执行验证 我们编译并且启动DrugShop.Main.exe,使用0001密码sa登录,我们就会在“统计报表”分组之中看到模块“模块程序集统计报表”: 打开这个模块: 设置开始时间和结束时间,点击“提取数据”按钮: OK,动态报表演示完成。 DrugShop案例之中同步提供了本例子的报表定义和导航、权限配置,请大家通过AgileEAS.NET SOA中间件官方网站的最新下载栏目进行下载。 五、联系我们 为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。 AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。 我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。 团队网站:http://www.agilelab.cn, AgileEAS.NET网站:http://www.smarteas.net 官方博客:http://eastjade.cnblogs.com QQ:47920381,AgileEAS.NET QQ群:113723486(AgileEAS SOA 平台)/上限1000人 199463175(AgileEAS SOA 交流)/上限1000人 212867943(AgileEAS.NET研究)/上限500人 147168308(AgileEAS.NET应用)/上限500人 172060626(深度AgileEAS.NET平台)/上限500人 116773358(AgileEAS.NET 平台)/上限500人 125643764(AgileEAS.NET探讨)/上限500人 193486983(AgileEAS.NET 平台)/上限500人 邮件:james@agilelab.cn,mail.james@qq.com, 电话:18629261335。
by 魏琼东
2013.12.18 13:37
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、管理信息系统开发之中的字典选择录入问题 AgileEAS.NET SOA中间件平台被广泛的应用于MIS类系统之中,可以说基于AgileEAS.NET SOA中间件开发MIS类真是得天读后,能大大的降低系统的开发难度和开发工作量。 有管理信息系统开发之中有很大一部分工作是实现界面的数据选择录入,比如商品进销存之中的根据输入码、拼音码、检索码查出某部分字典数据并且选择录入,如下图: 这是一个典型的字典选择输入应用,此类应用大量应用于比如像采购入库、采购申请之中的选择要采购的商品信息,商品信息存储在商品字典,本案例是选择药品字典信息,选择录入药品字典信息以便于进行药品入库业务处理。 在传统的此类应用开发过程之中我们需要自己处理当输入焦点在检索文本框之中进行输入的输入事件以及大量的界面、数据检索处理工具,费时费力,如果一个界面之中多出这样几个此类的应用场景,那么这个插件的代码的70%或者以上都用于应对此类场景,就形成了一种代码超长、界面控件、输入控件的事件处理麻烦的境界,如以下这个界面: 这个界面之中有五处此类场景的应用,当然,在一些应用之中可能有十个或者更多的这种场景,整个模块在应对此类情况的代码极其之中,也会慢慢的导致代码的可读性,可维护性越来越差。 三、TextBoxAutoComplete组件 AgileEAS.NET SOA中间件平台在经过多年痛苦的开发应用之中,从各种客户的反馈和开发应用的经验之中积累了一套解决此问题的方案,并创造性的实现了一个基于输入文本框的扩展解决方案,TextBoxAutoComplete组件及其之后的InputDict(输入字典)管理体系。 TextBoxAutoComplete组件:根据其名称我们可以简单的理解为TextBox的自动完成组件,那么其功能就是类似于baidu、google的搜索文本框功能: TextBox控件配合TextBoxAutoComplete完成类似上图你的自动完成检索功能,只不过baidu、google的搜索文本框展示的是搜搜索关键字,而AgileEAS.NET SOA中间件平台之中的TextBoxAutoComplete展现的是各种字典的录入选择表格,如下图: TextBox控件配合TextBoxAutoComplete可以完成对任意类型的字典信息的检索录入和自动完成功能。 四、输入字典管理 如果说要实现对任意类型的字典信息的检索录入和自动完成功能,仅TextBoxAutoComplete组件是不够的,为什么呢, 这会涉及到各类输入字典的录入数据规则、检索规则、数据窗口(DataWindow)的数据显示、排序规则、以及对数据源的过滤规则等等。 那么如何解决此问题呢,AgileEAS.NET SOA中间件平台提供了一个字典管理功能“输入字典”: 系统中维护着一组用于控制TextBoxAutoComplete组件的数据检索、显示的各项参数,其中包括如下重要信息: 字典编码:字典编码是一个Guid类型的值,其值必须是唯一的,也早用于控制TextBoxAutoComplete进行重要的重要依据,也是维系TextBox控件与TextBoxAutoComplete的唯一纽带。 数据类型:TextBoxAutoComplete组件工作是所依赖的AgileEAS.NET SOA中间件ORM实体对象类型,即输入字典的数据检索、显示必须依赖于ORM实体对象。 显示参数:数据窗口(DataWindow)的宽度、高度和每次可显示的记录行数。 是否启用缓存:即输入字典的检索行为是基于数据缓存进行的还是基于数据库检索进行的,因为我们的处理很多不常见化的字典信息,比如民族、省份这样的信息不会经常发生变化,AgileEAS.NET SOA中间件平台设计一套基于时间戳的实体缓存体系,如果选择启去缓存,只自动完成的检索不会请求数据库操作,以减少通信和数据库原因,如果选择启用了缓存,则必须要设定缓存属性。 缓存属性:用于控件缓存的增量更新的一个日期时间型字段/实体数据,并且必须在ORM实体的这个属性上面加上CacheUpdated标记: 1: /// <;summary>
2: /// 最后更新时间 。
3: /// <;/summary>
4: [Column("LMTime", "最后更新时间"),CacheUpdated]
5: [DisplayName("最后更新时间")]
6: public DateTime LMTime
7: {
8: get;
9: set;
10: }
并且需要在对就的字典管理/维护程序这中,当增加、修改某一行字典记录之后需要把对应的字段时间更新为最新的修改时间,那么缓存系统就会在一定的延迟规则之后获得这一更新记录。
显示属性:当TextBoxAutoComplete组件完成选择录入之后,显示在关联文本框内的值对应的ORM属性。
缓存间隔:输入字典记录本身的缓存间隔,即TextBoxAutoComplete引擎从数据库中更新对应的字典定义的周期,最小为0分钟,即TextBoxAutoComplete直接读取数据库中的字典记录。
码值属性:当TextBoxAutoComplete组件完成选择录入之后,用于执行其他业务的字典编码属性,一般情况下为字典的主键属性。
显示设置:用于设置DataWindow(数据窗口)之中显示的列信息记录,包括显示那些列、列标题、显示顺序等:
数据源筛选:在数据根据输入的条件检索之前的数据筛选功能:
上图的筛选条件表示仅选择Icd10bksn值为“B”的记录。
搜索条件:用于定制TextBoxAutoComplete组件在经过筛选之后的数据源上的检索行为,例如本文第二张图上的输入的“ys”之后的数据源检索行为:
目前倒提供基于字符串字段的三种检索条件:StartsWith(like ‘ys%’)、EndsWith(like ‘%ys’)和Contains(like ‘%ys%’),基本上能满足99的应用。
五、开发过程
首先我们要有规划,定将好数据实体、在必须的实体上定将好缓存更新标记,最新版本的ORM设计器提供了支持,我们就以DrugShop案例之中的药品字典实体以及在药品入库的录入检索为例进行说明。
药品字典我们考虑使用缓存,即把药品字典缓存在本地,以减少对服务器的请求和通信压力,以下是药品字典人的定义:
其中LMTime为最后更新时间,用于控制缓存的增量更新。
当然需要注意的是,并不是所有的输入字典信息都需要从缓存之中读取,对于实时可变的录入信息,如高品库存信息必须要进行实时读取,就没有必要使用缓存,因为对于这些实时数据,实体缓存的准确率非常之低。
设计好ORM实体模型并且生成代码、编译实体程序集之后,我们使用AgileEAS.NET平台的输入字典创建一个新的输入字典:
打上启用缓存复选框、选择设置缓存属性、码值属性、显示属性如下:
选择显示属性如下:
设置搜索条件如下:
复制下字典的编码,并且保存这个字典设置,接下来我们使用VS打开程序,在药品入库功能之中使用刚才定义好的字典,首页我们概引用平台的EAS.Data.Controls.dll程序集,并且在工具栏之中添加选择项=》选择EAS.Data.Controls.dll程序集,确保把TextBoxAutoComplete组件加载到工具箱之中:
然后我们像界面拖入TextBoxAutoComplete组件,完成后如下:
然后我们设计检索文本框txtSeach的属性如下:
这地方最重要的就是设计TextBox基于TextBoxAutoComplete1组件扩展出来的属性MetadateID,即扩展录入元数据ID,关联自我们定义好的输入字典。
是不是很简单,定义一个输入字典,程序代码之中只需要拖入一个组件,设计一个ID,即完成了需要大量编码才能完成的工具,名至实归的快速开发利器。
六、执行验证
我们编译并且启动DrugShop.Main.exe,使用0001密码sa登录,打开药品入库功能,来试一下是否能够执行自动完成:
OK,使用正常,没有任何问题。
DrugShop案例之中同步提供了本例子的字典定义和使用代码,请大家通过AgileEAS.NET SOA中间件官方网站的最新下载栏目进行下载。
七、联系我们
为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。
AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。
我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。
团队网站:http://www.agilelab.cn,
AgileEAS.NET网站:http://www.smarteas.net
官方博客:http://eastjade.cnblogs.com
QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET应用)/上限500人
172060626(深度AgileEAS.NET平台)/上限500人
116773358(AgileEAS.NET 平台)/上限500人
125643764(AgileEAS.NET探讨)/上限500人
193486983(AgileEAS.NET 平台)/上限500人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
by 魏琼东
2013.12.17 18:11
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、关于WinClient的外挂资源体系介绍 最初版本的AgileEAS.NET SOA中间件平台的运行容器/外壳是没有界面替换体系,最初只是提供了替换关键部分的图片、文字的功能,随着AgileEAS.NET SOA中间件被众多的客户应用到各行各业之后,我们从客户的应用反馈之中进行了大量的重构,其中运行容器也越来越开放,从最初令开放替换部分图片到慢慢开放可以自由修改其他的导航栏、Banner、状态栏、菜单、登录界面、关于界面,发生了很大的变化,同步我们也提供了Desktop、MDI、Dockable等多种界面风格,可以说客户有更我的选择了。 随着更多软件企业或组织加入应用AgileEAS.NET SOA中间件行业,有许多的客户已经不能满足于在这几种风格之中进行选择,希望能给自己最大的自定义空间和范围,提供了更换主界面的需求,我们积极的响应这种需求,并且提出了相关的解决方案,并且在2012年AgileEAS.NET SOA 5.0版本的时间向大家提供。 主界面替换被某一些我们服务于的客户所使用,但是对于广大的关心AgileEAS.NET SOA中间件的朋友来说,并不能得到我们细致并且一对一的服务,因为我们还没有变此部分内容给大家做过介绍和在公开的案例之中提供,所以对于社区之中的很多朋友来说,还能灵活的应用本部分内容。 下图是一个典型的AgileEAS.NET SOA中间件平台WinClient容器的主界面: 其中包含以下组成部分: 主菜单:界面最上部的主菜单,系统、视图和帮助。 导航菜单:通过菜单之中的菜单项可以打开指点的功能模块。 导航栏:同系统导航菜单、以树形或者其他形式展示。 状态栏:显示系统的一些状态信息,如操作消息、当前登录信息等。 内容区:除以上项目外的界面空白区域为业务工作区,即模块插件的工作区域,新打开的模块在经区域显示和工作。 启始页:特指工作区之中第一个打开的模块,当系统登录完成之后会自动打开一个模块,此模块称为起始页模块。 三、IResource接口 AgileEAS.NET SOA 中间件平台为了应用对运行容器的各个部分进行自定义和扩展,提供了一个资源接口IResource: 1: using System;
2:
3: namespace EAS.Explorer
4: {
5: /// <summary>
6: /// 运行容器的外壳资源。
7: /// </summary>
8: public interface IResource
9: {
10: /// <summary>
11: /// 获取应用系统的图标,用于替换主界面的图标。
12: /// </summary>
13: System.Drawing.Icon GetMainIcon();
14:
15: /// <summary>
16: /// 获取默认的模块图标,用于简约、经典风格的Tab页图标显示,也用于Desktop模式的桌面图标显示。
17: /// </summary>
18: System.Drawing.Image GetModuleIcon();
19:
20: /// <summary>
21: /// 获取桌面背景图像,用于Desktop模式的桌面模块。
22: /// </summary>
23: System.Drawing.Image GetDesktopImage();
24:
25: /// <summary>
26: /// 获取应用系统的导航控件,用于替换平台的导航控件。
27: /// </summary>
28: /// <returns>WinForm/WPF用户控件。</returns>
29: object GetNavigationControl();
30:
31: /// <summary>
32: /// 获取应用系统的Banner控件,用于替换平台的Banner条。
33: /// </summary>
34: /// <returns>WinForm/WPF用户控件。</returns>
35: object GetBannerControl();
36:
37: /// <summary>
38: /// 获取应用系统的Bottom控件,用于替换平台的状态栏。
39: /// </summary>
40: /// <returns>WinForm/WPF用户控件。</returns>
41: object GetBottomControl();
42:
43: /// <summary>
44: /// 获取WinForm/WPF容器的关于对话框,用于替换平台的关于对话框。
45: /// </summary>
46: /// <returns>WinForm/WPF窗体。</returns>
47: object GetAboutForm();
48:
49: /// <summary>
50: /// 获取WinForm/WPF容器的主界面,用于替换平台的主界面。
51: /// </summary>
52: /// <returns>WinForm/WPF窗体。</returns>
53: object GetMainShell();
54:
55: /// <summary>
56: /// 获取WinForm/WPF/Silverlight容器的登录对话框,用于替换平台的登录对话框。
57: /// </summary>
58: /// <returns>WinForm/WPF/Silverlight窗体。</returns>
59: ILoginForm GetLoginForm();
60:
61: /// <summary>
62: /// 获取WinForm/WPF容器的起始页/初始模块,用于替换平台的起始页。
63: /// </summary>
64: /// <returns>WinForm/WPF用户控件。</returns>
65: object GetStartModule();
66:
67: /// <summary>
68: /// 获取系统的名称,显示在运行环境的导航栏。
69: /// </summary>
70: /// <returns>应用系统名称。</returns>
71: string GetApplicationName();
72:
73: /// <summary>
74: /// 获取系统的标题,显示在运行环境的主窗口之上。
75: /// </summary>
76: /// <returns>应用系统名称。</returns>
77: string GetApplicationTitle();
78:
79: /// <summary>
80: /// 是否显示主菜单。
81: /// </summary>
82: bool DisplayMainMenu
83: {
84: get;
85: }
86:
87: /// <summary>
88: /// 是否显示导航工具条。
89: /// </summary>
90: bool DisplayNavigationTool
91: {
92: get;
93: }
94: }
95: }
实现AgileEAS.NET SOA 平台的资源替换即是实现IResource接口的过程,在我们DrugShop、SmartERP案例之中都有对应的实现项目DrugShop.Res和SmartERP.Res。
当然,光实现IResource还不购,还需要修改系统的配置文件,以便让资源生效:
1: <;!--资源-->
2: <object name="EAS.Explorer.Resource" assembly="DrugShop.Res" type="DrugShop.Res.Resources" LifestyleType="Singleton"/>
需要注意的是在EAS.Explorer.dll程序集之中还定义了以下两个接口,INavigation接口:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Text.RegularExpressions;
6:
7: namespace EAS.Explorer
8: {
9: /// <;summary>
10: /// 导航控件接口。
11: /// <;/summary>
12: public interface INavigation
13: {
14: /// <;summary>
15: /// 初始化导航。
16: /// <;/summary>
17: /// <;param name="m_GroupList">导航清单。</param>
18: /// <;param name="m_ModuleList">模块清单。</param>
19: void Initialize(IList<;INavigateGroup> m_GroupList, IList<INavigateModule> m_ModuleList);
20: }
21: }
ILoginForm接口:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace EAS.Explorer
7: {
8: /// <;summary>
9: /// 定义登记对话框接口,用于实现自定义登录。
10: /// <;/summary>
11: public interface ILoginForm
12: {
13: /// <;summary>
14: /// 是否已经通过登录验证。
15: /// <;/summary>
16: bool Passed { get; }
17: }
18: }
自定义的导航控件必须实现INavigation接口,以便于平台实现导航的初始化,方法Initialize由平台调用,传入当前系统登录人员所具有权限的模块清单及相关的导航清单。
自定我的登录界面必须实现ILoginForm,以便于系统判定登录界面是否完成了登录验证。
四、IMainShell接口和自定义主界面
第三节在介绍IResource接口的时候我们会发现其他有一个GetMainShell()方法,其用于获取资源实现之中的主界面定义,系统主界面必须是一个Form并且要实现IMainShell接口:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5:
6: namespace EAS.Explorer
7: {
8: /// <;summary>
9: /// 定义主界面接口,用于实现自定义主界面。
10: /// <;/summary>
11: public interface IMainShell
12: {
13: /// <;summary>
14: /// 初始化主界面,根据平台传入的模块清单和相关的导航分组初始化主界面。
15: /// <;/summary>
16: /// <;param name="m_GroupList">导航清单。</param>
17: /// <;param name="m_ModuleList">模块清单。</param>
18: void InitializeShell(IList<;INavigateGroup> m_GroupList, IList<INavigateModule> m_ModuleList);
19:
20: /// <;summary>
21: /// 加载/打开指定模块,响应系统的模块打开操作。
22: /// <;/summary>
23: /// <;param name="module">模块实例。</param>
24: void OpenModule(object module);
25:
26: /// <summary>
27: /// 关闭当前模块(活动的模块)。
28: /// <;/summary>
29: void CloseModule();
30:
31: /// <summary>
32: /// 关闭指定模块。
33: /// <;/summary>
34: /// <;param name="module">模块实例。</param>
35: void CloseModule(object module);
36:
37: /// <summary>
38: /// 切换导航,展开、隐藏导航,主界面没有导航的隐藏、展开需求可以不处理。
39: /// <;/summary>
40: void SwitchNavigation();
41:
42: /// <summary>
43: /// 当前活动插件/模块。
44: /// <;/summary>
45: object ActiveAddIn
46: {
47: get;
48: }
49:
50: /// <;summary>
51: /// 已打开的插件/模块集合。
52: /// <;/summary>
53: List<;object> AddIns
54: {
55: get;
56: }
57: }
58: }
其中InitializeShell方法:用于平台传入当前系统登录人员所具有权限的模块清单及相关的导航清单,由自定义界面实现界面的初始化。
OpenModule方法:用于实现对模块的打开,平台传入要打开的模块,由主界面进行处理,实现主界面对模块的动态加载。
CloseModule方法:用于关闭已经打开的模块,并对界面进行清理。
SwitchNavigation方法:切换导航,展开、隐藏导航,主界面没有导航的隐藏、展开需求可以不处理。
ActiveAddIn属性:向平台返回当前的活动模块。
AddIns属性:向平台返回已加载的模块清单。
四、自定义界面实例
近期 有朋友建议使用devcomponents或者DotNetBar为大家演示一下如何自定义平台的主体界面,参考了网有对devcomponents和DotNetBar相关的资料之后我们选择了较为轻量级的DotNetBar为大家演示如何替换系统的主界面。
我们使用DotNetBar所提供的一些控件实现了以下三种风格的主体界面:
第一种是Win7/Ribbon风格的主界面RibbonShell,如下图所示:
其对应代码为DrugShop案例之中的DrugShop.Res项目之中的RibbonShell.cs程序文件。
第二种为类似Visual Studio界面风格的DockableShell,其效果如下:
其对应代码为DrugShop案例之中的DrugShop.Res项目之中的DockableShell.cs程序文件。
第三种为不包含导航栏的简单MDI界面风格的TabShell,其效果如下:
其对应代码为DrugShop案例之中的DrugShop.Res项目之中的TabShell.cs程序文件。
五、关于代码
以上介绍了三种风格的自定义主界面的例子,例子不多,也不复杂,或许也不能满足所有人的美观度需求和功能需求,其目的在于抛砖引用,希望借此文让更多的开发者加入到我们的行列之中来,做出更加美观大方的界面。
关于以上三种风格自定义界面代码,我们已经合并到DrugShop案例和SmartEPR案例之中,请大家在AgileEAS.NET SOA中间件官方网站的最新下载栏目进行下载。
在启动自定义界面的过程之中切记要修改资源项目之中IResource的实现代码之中的GetMainShell方法代码:
1: public object GetMainShell()
2: {
3: return new RibbonShell(); //Ribbon风格自定义界面。
4: //return new DockableShell(); //Dockable风格自定义界面。
5: //return new TabShell(); //TabMdi风格自定义界面。
6: //return null; //使用AgileEAS.NET SOA平台自带界面。
7: }
本文所使用的是DotNetBar115版本,有关于DotNetBar115请通过http://112.74.66.105/downloads/DotNetBar115.rar下载。
六、联系我们
为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。
AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。
我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。
团队网站:http://www.agilelab.cn,
AgileEAS.NET网站:http://www.smarteas.net
官方博客:http://eastjade.cnblogs.com
QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET应用)/上限500人
172060626(深度AgileEAS.NET平台)/上限500人
116773358(AgileEAS.NET 平台)/上限500人
125643764(AgileEAS.NET探讨)/上限500人
193486983(AgileEAS.NET 平台)/上限500人
邮件:james@agilelab.cn,mail.james@qq.com,
电话:18629261335。
by 魏琼东
2013.11.11 13:38
一、前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。 AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。 AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。 二、关于SmartERP.NET SmartERP.NET 是一套简单的电脑耗材分销ERP系统,其实说是ERP系统还有点夸张,但是最初的开发者都已经这么叫了,那么我们也就也就这么理解吧,SmartERP.NET 原本是我们的一个客户在2011年为其一个客户/朋友开发的一套软件,当时客户人员的开发都是以VB6.0进行开发,客户打算把开发语言换到C#上,基于我们平台进行技术预研,并且打算基于AgileEAS.NET SOA中间件平台进行应用开发,正好当然有一个朋友是做某品牌电脑大区经销的,双方双有所需,正好就基于我们AgileEAS.NET SOA中间件开发了这套简单的ERP系统,其目的有两个,一个是解决客户的需求、另一个是锻炼一下自己的开发人员。 以下是系统主界面: 什么是ERP: ERP是Enterprise Resource Planning(企业资源计划)的简称,是一种主要面向制造行业进行物质资源、资金资源和信息资源集成一体化管理的企业信息管理系统。 ERP是一个以管理会计 为核心可以提供跨地区、跨部门、甚至跨公司整合实时信息的企业管理软件。 参见百度百科: http://baike.baidu.com/subview/109408/7177679.htm SmartERP.NET货物采购入库流程 SmartERP.NET 货物销售流程 三、系统客户端及服务端配置 有关程序如何配置请参见 魏琼东的文章 AgileEAS.NET SOA 中间件平台 5.2 发布说明-包含Silverlight及报表系统的开源代码下载 AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(一):下载平台并基于直连环境运行 AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(二):配置WinClient分布式运行环境 AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(三):配置ActiveXForm运行环境 AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(四):开源的Silverlight运行容器的编译、配置 四、完整的应用-实现完整的ERP 本文我们将实现一个类似电脑城的货物ERP系统,主界面如下:(登录账户:erp-admin 密码为空) 五、下载源代码编译配置运行 本案例源代码请通AgileEAS.NET SOA中间件官方网站的最新下载中的栏目进行下载或者从SVN中获取最新版本的源代码: 下载或者SVN-OUT得到代码之后我们来看一下代码文建构: 一级目录 二级目录 说明 Code 程序源代码 SmartERP.Entities 实体(ORM)代码 SmartERP.BLL.Contracts 业务契约定义代码 SmartERP.BLL.Host 数据实现代码 SmartERP.Common 公用类库 SmartERP.WinUI 界面层代码 db 用户数据库文件,SQL Server数据库 Documents 文档,用户模型文档 Bin 引用程序集及编译输出 用VS2010或者VS2012打开Code目录之中的SmartERP.sln解决方案进行编译,编译成功之后我们开始进行配置。 打开Bin目录之中的SmartERP.Main.exe.config进行客户端配置: 修改红色部分连接字符串 六、统计报表 SmartERP示例中使用的是Gird++ 报表程序 开发管理 -- GR报表管理 数据报表就是写SQL语句查询数据库 获取结果 对象报表就是通过查询内存对象 获取结果 报表的制作:为了降低入门的难度,本编介绍的是用数据报表也就是写SQL语句查询数据库的方式,对象报表将在以后的文章中介绍。 1.新建-数据报表-查询语句中 写入要查询的报表sql语句 2.报表设计界面 3.设置要显示的是哪张报表,根据GUID 4.编写查询语句 并设置过滤条件 5. 报表预览效果 由于时间仓促,程序难免会有Bug,望大家谅解。 截止这里 我们的教程就已经写完了,表结构及相关说明请参阅SmartErp.NET下载包里面的相关文档。 最后感谢SmartErp.NET的原始作者陶虎刚同学,也感谢对原始代码、文档进行整理升级,以及本文的作者计文析同学。 七、联系我们 为完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。 AgileEAS.NET SOA中间件平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。 我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。 团队网站:http://www.agilelab.cn, AgileEAS.NET网站:http://www.smarteas.net 官方博客:http://eastjade.cnblogs.com QQ:47920381,AgileEAS.NET QQ群:113723486(AgileEAS SOA 平台)/上限1000人 199463175(AgileEAS SOA 交流)/上限1000人 120661978(AgileEAS.NET 平台交流)/上限1000人 212867943(AgileEAS.NET研究)/上限500人 147168308(AgileEAS.NET应用)/上限500人 172060626(深度AgileEAS.NET平台)/上限500人 116773358(AgileEAS.NET 平台)/上限500人 125643764(AgileEAS.NET探讨)/上限500人 193486983(AgileEAS.NET 平台)/上限500人 邮件:james@agilelab.cn,mail.james@qq.com, 电话:18629261335。