一步一步学WF系列(八)——改善登录程序(下)

1.  摘要

在上文中,我们对登录程序做了一个简单的过程建模。

在本文中,我们就来自己一步一步去改善这个登录程序。

2.  设定接口

我们先来看看整个程序的建模图,我们在上一章已经给出了分析,这里只是引用一下:

image

在这段程序中,我们的工作流与外部发生了三次交互:

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:工作流去调用外部的方法。

那么,我们的流程是:

外部触发工作流验证 —> 工作流去调用验证 —> 接收信号 —> 工作流判断信号 —> 根据信号去调用对应的外部方法

好,根据这个流程,我们可以很清晰地去完成工作流建模了:

image

不知道您明白了么?

我们继续往下做。

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("登录成功");
}

#endregion

5. 完成工作流

接下来,我们可以完成工作流端的工作了。

首先,我们将handlerExternalEventActivity绑定我们的IEvent接口中的Login事件:

image

接下来,我们将callExternalMethodActivity绑定到IValidator的Validate方法。

image

在这里,我们一定注意到了user和ReturnValue。我们来对这两个属性进行解释。我们首先来回顾一下Validator方法:

int Validate(UserInfo user);

他需要传入一个UserInfo类型的user参数,所以我们需要对user进行相应的配置:

点击user右侧的….,你会看到这样的窗口:

image

点击确定后,我们会在后台看到这样的代码:

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);
    }
}

这时,这个参数就可以在工作流和外部程序之间传递了。

接下来,我们需要设置返回的参数,道理是一样的:

image

好了,这个活动的设置结束。

我们接着去想,这个方法返回了三个可能,如果用户名密码为空返回0,用户名密码错误返回-1,用户名密码正确返回1。

对,接下来我们该去配置if-else活动了。

还记得刚才我们把返回值设置成了state吧,那么我们就应该对这个state值进行判断,然后把这个作为if-else的条件:

image

方便起见,我们用声明性规则条件去判断:

image

image

接下来对不同条件所要执行的活动进行一个配置:

image

image

image

在工作流的后台代码中,我们仅仅需要在工作流被触发的时候,将应用程序传递进行来的参数转换为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. 前台窗口

image

我们做一个简单的登录界面。

接下来看看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. 测试结果

我们运行程序进行测试:

image

image

image

9. 总结

回顾我们的代码:

我们的后台代码摆脱了那些复杂的判断。

我们的工作流和具体的实现解耦。

设计师去负责流程控制,小兵去写实现代码。多线程并发执行,很好很好。

但是,这个登录很完善了么?不见得。我们会在今后的文章中对他继续完成改进。

这篇文章就到这,感谢大家的关注。

项目源码:/Files/xinyuperfect/WorkflowConsoleApplication9.rar

posted @ 2009-04-23 17:20  飞林沙  阅读(2385)  评论(11编辑  收藏  举报