一步一步学WF系列(八)——改善登录程序(下)
1. 摘要
在上文中,我们对登录程序做了一个简单的过程建模。
在本文中,我们就来自己一步一步去改善这个登录程序。
2. 设定接口
我们先来看看整个程序的建模图,我们在上一章已经给出了分析,这里只是引用一下:
在这段程序中,我们的工作流与外部发生了三次交互:
A. 外部调用工作流让工作流开始执行
B. 工作流请求外部的验证程序
C. 工作流验证结果后,要通过外部去执行相应的操作。
于是,我们在这里建立三个类库,每个类库包含一个接口。
相应的,我们还记得我们平时做ASP.NET程序吧?我们层与层之间传递的应该是一个个实体,这里也是一样。
让我们一步一步来做,首先建立一个类库,类库中包含一个实体类:
using System.Workflow.Activities; namespace DAO { [Serializable] public class UserInfo:ExternalDataEventArgs { private Guid guid; private string userName; private string password; public Guid Guid { get { return guid; } set { guid = value; } } public string UserName { get { return userName; } set { userName = value; } } public string Password { get { return password; } set { password = value; } } public UserInfo(Guid guid, string userName, string password) : base(guid) { this.userName = userName; this.password = password; } } }
接下来,我们去新建三个类库,每个类库中包含一个接口,负责工作流和外部程序的交互:
A. Interface类库中IEvent接口(外部通过他来触发工作流的执行):
namespace Interface { [ExternalDataExchange] public interface IEvent { event EventHandler<ExternalDataEventArgs> Login; } }B. Validator类库中的IValidate接口(工作流通过找到接口去验证合法性):
namespace Validator { [ExternalDataExchange] public interface IValidate { int Validate(UserInfo user); } }C. UserAlert类库中的IAlert接口(工作流通过找到接口来针对每种情况执行相应的代码):
namespace UserAlert { [ExternalDataExchange] public interface IAlert { void AlertNull(); void AlertFailed(); void AlertSuccess(); } }接口建好了,让我们在工作流中去模拟这个建模过程。
3. 工作流建模
我们复习一下之前的知识:
HandlerExternalEvent:外部通过他来调用工作流内部的事件。
CallExternalMethodEvent:工作流去调用外部的方法。
那么,我们的流程是:
外部触发工作流验证 —> 工作流去调用验证 —> 接收信号 —> 工作流判断信号 —> 根据信号去调用对应的外部方法
好,根据这个流程,我们可以很清晰地去完成工作流建模了:
不知道您明白了么?
我们继续往下做。
4. 实现接口方法
工作流有一个优点:每个成员可以完全独立地去做他们自己的工作,而不是彼此依赖。
让我们在这里体会下。
首先,让我们先完成验证的类,新建一个UserValidator类库,里面有个Validator类,他实现了 IValidator接口,这里我只写个简化版的验证,大家如果感兴趣,可以自己连数据库:
namespace UserValidator { public class Validator:IValidate { #region IValidate 成员 public int Validate(UserInfo user) { string userName = user.UserName; string password = user.Password; if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password)) { return 0; } if (userName.Equals("admin") && password.Equals("admin")) { return 1; } return -1; } #endregion } }接下来,我们新建一个Winform程序,让Winform实现IAlert接口:
#region IAlert 成员 public void AlertNull() { MessageBox.Show("用户名和密码不能为空"); } public void AlertFailed() { MessageBox.Show("用户名或密码错误"); } public void AlertSuccess() { MessageBox.Show("登录成功"); } #endregion5. 完成工作流
接下来,我们可以完成工作流端的工作了。
首先,我们将handlerExternalEventActivity绑定我们的IEvent接口中的Login事件:
接下来,我们将callExternalMethodActivity绑定到IValidator的Validate方法。
在这里,我们一定注意到了user和ReturnValue。我们来对这两个属性进行解释。我们首先来回顾一下Validator方法:
int Validate(UserInfo user);他需要传入一个UserInfo类型的user参数,所以我们需要对user进行相应的配置:
点击user右侧的….,你会看到这样的窗口:
点击确定后,我们会在后台看到这样的代码:
public static DependencyProperty userProperty = DependencyProperty.Register("user", typeof(DAO.UserInfo), typeof(WorkflowConsoleApplication9.Workflow1)); [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)] [BrowsableAttribute(true)] [CategoryAttribute("参数")] public UserInfo user { get { return ((DAO.UserInfo)(base.GetValue(WorkflowConsoleApplication9.Workflow1.userProperty))); } set { base.SetValue(WorkflowConsoleApplication9.Workflow1.userProperty, value); } }这时,这个参数就可以在工作流和外部程序之间传递了。
接下来,我们需要设置返回的参数,道理是一样的:
好了,这个活动的设置结束。
我们接着去想,这个方法返回了三个可能,如果用户名密码为空返回0,用户名密码错误返回-1,用户名密码正确返回1。
对,接下来我们该去配置if-else活动了。
还记得刚才我们把返回值设置成了state吧,那么我们就应该对这个state值进行判断,然后把这个作为if-else的条件:
方便起见,我们用声明性规则条件去判断:
接下来对不同条件所要执行的活动进行一个配置:
在工作流的后台代码中,我们仅仅需要在工作流被触发的时候,将应用程序传递进行来的参数转换为UserInfo类型即可:
private void handleExternalEventActivity1_Invoked(object sender, ExternalDataEventArgs e) { UserInfo userInfo = e as UserInfo; if (userInfo != null) { user = userInfo; } }6. 工作流运行时工厂
因为我们的工作流运行时要加载很多服务,而这些服务来自不同的类库,所以我们把创建工作流运行时的任务封装起来,形成一个简易的工作流运行时工厂,新建一个类库:
namespace WorkflowCreator { public class Creator { public static WorkflowRuntime CreateRuntime() { WorkflowRuntime runtime = new WorkflowRuntime(); ExternalDataExchangeService service = new ExternalDataExchangeService(); runtime.AddService(service); service.AddService(new UserValidator.Validator()); return runtime; } } }7. 前台窗口
我们做一个简单的登录界面。
接下来看看Winform1.cs的完整代码:
public partial class Form1 : Form,IEvent,IAlert { private WorkflowRuntime runtime; private ExternalDataExchangeService service; private WorkflowInstance instance; public Form1() { InitializeComponent(); runtime = WorkflowCreator.Creator.CreateRuntime(); service = new ExternalDataExchangeService(); runtime.AddService(service); service.AddService(this); } private void button1_Click(object sender, EventArgs e) { instance = runtime.CreateWorkflow(typeof(WorkflowConsoleApplication9.Workflow1)); instance.Start(); UserInfo user = new UserInfo(instance.InstanceId, this.textBox1.Text, this.textBox2.Text); Login(null, user); } #region IEvent 成员 public event EventHandler<System.Workflow.Activities.ExternalDataEventArgs> Login; #endregion #region IAlert 成员 public void AlertNull() { MessageBox.Show("用户名和密码不能为空"); } public void AlertFailed() { MessageBox.Show("用户名或密码错误"); } public void AlertSuccess() { MessageBox.Show("登录成功"); } #endregion }8. 测试结果
我们运行程序进行测试:
9. 总结
回顾我们的代码:
我们的后台代码摆脱了那些复杂的判断。
我们的工作流和具体的实现解耦。
设计师去负责流程控制,小兵去写实现代码。多线程并发执行,很好很好。
但是,这个登录很完善了么?不见得。我们会在今后的文章中对他继续完成改进。
这篇文章就到这,感谢大家的关注。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!