zoukankan      html  css  js  c++  java
  • 应用AOP简化WinForm的异步操作——EntLib PIAB实现

    回首征途

    在上一篇《应用AOP简化WINFORM的异步操作——PostSharp实现》中,实现了通过AOP的方式隔离BackgroundWorker的调用。

    正如有朋友不倾向PostSharp的编译时代码织入方式,我也没在日常项目中使用过PostSharp。

    虽然问题可能不大,弃用它也只是重新编译一遍。

    但最近尝试Enterprise Library PIAB模块来实现相同的功能,还是发现了一些细节问题。

    一鼓作气

    与PostSharp不同,PIAB是以动态代理的方式来实现的。那么我们不能直接沿用Form中的代码,需要添加一个代理类来实现WorkThread。好吧,那么我们顺便引入MVP模式,通过Presenter类来作代理。

    预想中的代码:

    Model:

    public class ArticleModel
    {
    public int Id { get; set; }

    public string Title { get; set; }

    public static List<ArticleModel> GetAll()
    {
    //todo
    }
    }

    View:

    public interface IArticleView
    {
      List<ArticleModel> Articles { set; }
    }
    public class ArticleForm : IArticleView
    {
      private readonly ArticlePresenter presenter;
      public List<ArticleModel> Articles
    {
    set
    {
    //todo:binding
    }
    }
    }


    Presenter:

    public class ArticlePresenter 
    {

    private readonly IArticleView view;

    private List<ArticleModel> articles;


    public ArticlePresenter(IArticleView view)
    {
    this.view = view;
    }

    [WorkThread]
    public void Download()
    {
      //todo:loading data
               Binding();
    }
             [GuiThread]
    private void Binding()
    {
    view.Articles = articles;
    }
    }
      


     然后基于PIAB重新实现WorkThreadAttribute&GuiThreadAttribute

     以WorkThread为例,先要创建一个CallHandler

    View Code
    public class WorkThreadHandler : ICallHandler
    {
    #region Constants and Fields

    private readonly bool reportProgress;

    private IBlockDialog blockForm;

    private BackgroundWorker worker;

    private IMethodReturn methodReturn;


    #endregion

    #region Constructors and Destructors

    public WorkThreadHandler(bool reportProgress)
    {
    this.reportProgress = reportProgress;
    }


    #endregion

    #region Public Properties

    public int Order { get; set; }

    #endregion

    #region Public Methods

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
    if (input == null)
    {
    throw new ArgumentNullException("input");
    }
    if (getNext == null)
    {
    throw new ArgumentNullException("getNext");
    }

    this.worker = new BackgroundWorker();
    this.worker.DoWork += this.worker_DoWork;
    this.worker.RunWorkerCompleted += this.worker_RunWorkerCompleted;
    if (this.reportProgress)
    {
    this.worker.WorkerReportsProgress = true;
    this.worker.ProgressChanged += this.worker_ProgressChanged;
    }
    this.worker.RunWorkerAsync(new object[] { input, getNext });

    this.blockForm = input.Target as IBlockDialog;
    if (this.blockForm != null)
    {
    this.blockForm.Handler = this;
    this.blockForm.Block();
    }

    while (worker.IsBusy)
    {
    Thread.Sleep(500);
    }
    return methodReturn;
    }

    #endregion

    #region Methods

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
    var args = e.Argument as object[];
    var input = args[0] as IMethodInvocation;
    var getNext = args[1] as GetNextHandlerDelegate;
    //调用方法实现
    methodReturn = getNext()(input, getNext);
    }

    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
    this.blockForm.ShowProcess(e.ProgressPercentage);
    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    if (this.blockForm != null)
    {
    this.blockForm.UnBlock();
    }
    }

    #endregion
    }

     实现Attribute:

    View Code
    [AttributeUsage(AttributeTargets.Method)]  
    public class WorkThreadAttribute : HandlerAttribute
    {
    private bool reportProgress;
    private int order;

    public WorkThreadAttribute(bool reportProgress)
    {
    this.reportProgress = reportProgress;
    }

    public WorkThreadAttribute(bool reportProgress, int order)
    : this(reportProgress)
    {
    this.order = order;
    }
    public WorkThreadAttribute() : this(false)
    {
    order = 1;
    }


    #region Overrides of HandlerAttribute

    public override ICallHandler CreateHandler(IUnityContainer container)
    {
    var handler = new WorkThreadHandler(reportProgress){Order = order};
    return handler;
    }

    #endregion
    }
      


     GuiThreadHandler&GuiThreadAttribute如法炮制,做好Form,可以开始调试了!

    山重水复

    预料中的情况发生,甫一运行即遇上‘Cross-thread operation not valid’的异常。

    是下面一段的Binding方法调用产生的,WorkThread试图访问UI:

     [WorkThread]
    public void Download()
    {
    	//todo:loading data
         Binding();
    }
    [GuiThread]
    private  void Binding()
    {
      view.Articles = articles;
    }

     为什么这个代码在PostSharp环境下能正常运行呢,因为PostSharp在编译时织入了它的拦截代码,客观上起到了GuiThread与WorkThread隔离的作用。

    尝试了许多方法来规避这个问题,未果。

    最终将目光聚焦到BackgroundWorker本身上。

    通常情况下,我们通过BackgroundWorker.DoWork事件来加载数据,RunWorkerCompleted事件来展示数据。RunWorkerCompleted事件是在窗体主线程上实现的。

    目前,我在RunWorkerCompleted事件上,只做了关闭Block Dialog的操作,何不同时做数据绑定呢!

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    	if (this.blockForm != null)
    	{
    		this.blockForm.UnBlock();
    	}
    }
    
    
    
    
    旧城改造
    插叙:在完善代码之前,由于对上一篇引入的观察者不爽,先来修理一下。观察者模式常用于一对多的关系中,但在此例中BlockDialog和WorkHandler是一对一的关系,可以不用观察者,改为直接引用:
    public interface IBlockDialog
    {
    IWorkThreadHandler Handler { get; set; }
    void Block();

    void UnBlock();

    void ShowProcess(int percentage);

    void ShowStatus(string status);
    }

    public interface IWorkThreadHandler
    {
    void Invoke(MethodInterceptionArgs args,bool reportProgress);

    void ReportProgress(int percentage);
    }


     其中Handler为新增属性,用于BlockDialog to WorkHandler的映射。 

    IWorkThreadHandler为新增接口,通过ReportProgress方法向BackgroundWorker报告进程。
    
    
    
    
    柳暗花明

    现在来重新设计接口与Presenter的基类

    IBlockView:Block Dialog界面的抽象
    
    IBlockDialog:Presenter与Block Dialog交互的抽象,具有IBlockView的所有行为,WorkHandler需要通过Presenter来操控Block Dialog的弹出、关闭、进度条展示。
    IAsyncPresenter:继承IBlockDialog,同时包含一个IBlockView。
    AsyncPresenterBase:继承MarshalByRefObject,用于PIAB拦截。同时实现IAsyncPresenter。
    
    
    回到数据绑定的问题上来,当前已经在BackgroundWorker.RunWorkerCompleted事件中调用了IBlockDialog.UnBlock,即关闭Block Dialog。
    那么在IBlockDialog.UnBlock的实现代码中做数据绑定即可。
    为AsyncPresenterBase基类添加虚方法:
    protected virtual void Binding()
    {
    }
    Block Dialog关闭时,调用Binding方法。
    public void UnBlock()
    {
    	this.BlockView.UnBlock();
    	this.Binding();
    }
    所有派生类重写Binding方法即可。
    最终ArticlePresenter如下:
    [WorkThread]
    public void Download()
    {
    articles = ArticleModel.GetAll();
    Thread.Sleep(3000);
    }


    protected override void Binding()
    {
    view.Articles = articles;
    }
    
    

      1、删除GuiThreadAttribute

      2、重写Binding方法,WorkThread不直接调用Binding方法

     
    共享成果
    Demo:https://files.cnblogs.com/cnsharp/WinForm.AOP.PIAB.7z
    
    

     

    作者:CnSharp.com
    本文版权归CnSharp.com和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    php 本地 备份远程mysql和mdb 多任务只执行一次
    c# 监控服务器上传木马(包含可疑文件)
    jquery 简短 右键菜单 兼容ie6 ie7 ie8 firefox chrome
    分解从身份证中读回的户籍地址
    备份mysql(一表一文件)
    THINKPHP 3.0 整合KINDEDITOR 4.05
    c# 仿照计划任务(定时提示、定时运行程序、定时打开url(前台/后台))/每天/每周/每月/一次 多时间段
    php基本操作echo
    c# 扫描可疑文件(找到木马)(简)
    GOOGLE 地图,查询地名,移动标记,生成静态地图
  • 原文地址:https://www.cnblogs.com/cnsharp/p/2271297.html
Copyright © 2011-2022 走看看