2010-01-17

CAB & SCSF - Part 02: WorkItems

-->
WorkItems

Part 2: Overview khái niệm WorkItem.

WorkItems – Basic Concepts

Theo tài liệu của Microsoft mô tả là một run-time container chứa các component hợp tác với nhau thực hiện chức năng của một use case (a run-time container of components that are collaborating to fulfill a use case). Có thể xem WorkItems là các class trong đó có chứa các collection của các class khác. WorkItem trích từ metadata như sau:

using Microsoft.Practices.CompositeUI.Collections;
using Microsoft.Practices.CompositeUI.Commands;
using Microsoft.Practices.CompositeUI.EventBroker;
using Microsoft.Practices.CompositeUI.SmartParts;
using Microsoft.Practices.CompositeUI.Utility;
using Microsoft.Practices.ObjectBuilder;
using System;
using System.ComponentModel;
using System.Diagnostics;

namespace Microsoft.Practices.CompositeUI
{
    public class WorkItem : IBuilderAware, IDisposable
    {
        public WorkItem();

        public ManagedObjectCollection<Command> Commands { get; }
        public ManagedObjectCollection<EventTopic> EventTopics { get; }
        public string ID { get; set; }
        protected Builder InnerBuilder { get; }
        protected IReadWriteLocator InnerLocator { get; }
        public ManagedObjectCollection<object> Items { get; }
        [Dependency(NotPresentBehavior = NotPresentBehavior.ReturnNull)]
        [Browsable(false)]
        public WorkItem Parent { get; set; }
        [Browsable(false)]
        public WorkItem RootWorkItem { get; }
        public ServiceCollection Services { get; }
        public ManagedObjectCollection<object> SmartParts { get; }
        public State State { get; }
        public WorkItemStatus Status { get; }
        public TraceSource TraceSource { set; }
        public UIExtensionSiteCollection UIExtensionSites { get; }
        public ManagedObjectCollection<WorkItem> WorkItems { get; }
        public ManagedObjectCollection<IWorkspace> Workspaces { get; }

        public event EventHandler Activated;
        public event CancelEventHandler Activating;
        public event EventHandler Deactivated;
        public event CancelEventHandler Deactivating;
        public event EventHandler Disposed;
        public event EventHandler<DataEventArgs<string>> IdChanged;
        public event EventHandler Initialized;
        public event EventHandler RunStarted;
        public event EventHandler Terminated;
        public event EventHandler Terminating;

        public void Activate();
        protected internal void BuildUp();
        protected virtual Command CreateCommand(Type t, string name);
        protected virtual EventTopic CreateEventTopic(Type t, string topicName);
        public void Deactivate();
        public void DeleteState();
        public void Dispose();
        protected virtual void Dispose(bool disposing);
        protected internal void FinishInitialization();
        public TSmartPartInfo GetSmartPartInfo<TSmartPartInfo>(object smartPart) where TSmartPartInfo : ISmartPartInfo;
        protected internal void InitializeRootWorkItem(Builder builder);
        protected virtual void InitializeServices();
        [InjectionMethod]
        public void InitializeWorkItem();
        public void Load();
        protected virtual void OnActivated();
        protected virtual void OnActivating(CancelEventArgs args);
        public virtual void OnBuiltUp(string id);
        protected virtual void OnDeactivated();
        protected virtual void OnDeactivating(CancelEventArgs args);
        protected virtual void OnDisposed();
        protected virtual void OnIdChanged();
        protected virtual void OnInitialized();
        protected virtual void OnObjectAdded(object item);
        protected virtual void OnObjectRemoved(object item);
        protected virtual void OnRunStarted();
        public virtual void OnTearingDown();
        protected virtual void OnTerminated();
        protected virtual void OnTerminating();
        public void RegisterSmartPartInfo(object smartPart, ISmartPartInfo info);
        public void Run();
        public void Save();
        public void Terminate();
    }
}

Dễ dàng nhận thấy WorkItem có các 3 collection như sau:
1.      Items là một collection loại object nên có thể contains tất cả mọi thứ.
2.      Services collection chứa các CAB services (sẽ đề cập sau)
3.      WorkItems collection là một collection base trên WorkItem. Đây là các child WorkItems. Collection này thể hiện Composite pattern mà chúng ta đã tìm hiểu.
4.      Các collection khác SmartParts, UIExtensionSites, Workspaces.

WorkItem còn có State để theo dõi sự thay đổi trạng thái implement ISerializable và Status chỉ định là active hay inactive.

Theo mô tả ở trên sẽ thấy có rất nhiều thuật ngữ chưa đề cập và các thuật ngữ này rất mới và khó hiểu.

Container Hierarchy và Root WorkItem

Các WorkItems có thể biểu diễn dưới dạng phân cấp. Theo như lab 1 thì program có một RootWorkItem và Shell là một instance của ShellForm. Blue module và Red module sẽ được load vào RootWorkItem. WorkItem child có thể được truy cập qua code dạng như sau:

this.WorkItem.WorkItems["SpecificName"]

Đến đây chúng ta có thể mơ hồ nhận ra mối quan hệ giữa CAB và Composite pattern mà chúng ta đã tìm hiểu.

WorkItems và FormShellApplication
Ví dụ như trong lab về module loader của Part 1, Program chứa top-root WorkItem và được truy cập thông qua code:
this.RootWorkItem

Program cũng chứa một Shell có thể truy cập qua code:
this.Shell

Notes: hiện tại lab module loader Part 1 vẫn chưa thực hiện dùng hay truy cập thông qua các properties này. Các bài labs tiếp theo sẽ thực hiện trên các property này.

Trong phần tiếp theo chúng ta sẽ tìm hiểu cách thực hiện việc load modules cụ thể như thế nào. Đối tượng để tìm hiểu là Dependency Injection, một khái niệm quan trọng.

CAB & SCSF - Part 01: Module loader

-->
Module Loader
Part 1: giới thiệu tính năng module loader của CAB

Chuẩn bị lab về module loader
Mục đích giới thiệu tính năng tìm và load module qua ProfileCatalog.xml

- Bước chuẩn bị, tạo solution Part01A.sln có các project như sau:
Project
Type/Desc
Blue
Form application, có một form là BlueForm back color là màu xanh. Assembly name là Blue, namespace là Blue.
Red
Form application, có một form là RedForm back color màu đỏ. Assembly name là Red, namespace là Red.
Shell
Một form application default. Assembly name là Shell, namespace là Shell.

- Nếu chúng ta đã thực hiện cài đặt Composite UI Application Block thì thư mục mặc định sẽ nằm tại C:\Program Files\Microsoft Composite UI App Block với C là system drive. Chúng ta sẽ thực hiện import các project CAB nằm trong C:\Program Files\Microsoft Composite UI App Block\CSharp\Source\CompositeUI vào solution. Nếu sử dụng Visual Studio 2008 chúng ta cần convert các project này theo thứ tự từ ObjectBuilder trước. Tuy nhiên để đơn giản chỉ cần DLL files, tạo thư mục ThirdParty cùng cấp với thư mục chứa solution Part01A, copy vào đó 3 files sau:
Microsoft.Practices.CompositeUI.dll
Microsoft.Practices.CompositeUI.WinForms.dll
Microsoft.Practices.ObjectBuilder.dll

- Tạo solution folder ThirdParty: thay vì import các project của CAB chúng ta thực hiện import 3 DLL files đã nói ở trên.



Cấu trúc thư mục


- Add reference tới 3 files trong ThirdParty cho các project Red, BlueShell

Các khái niệm:
Module: đơn giản có thể coi là một ‘component’ hay một ‘application’. Là một block of code thông thường là một UI có thể hiển thị trên một window thường nhưng không reference tới một module khác. Có thể tưởng tượng module là project trong .NET solution. Tổng kết là một standalone project trong đó có một composite user interface.

Shell: một host form chứa các composite user interface.

Nội dung của module - Tìm hiểu ModuleInit
- Thêm vào trong mỗi solution một file như sau (BlueModuleInit.cs):
using Microsoft.Practices.CompositeUI;
namespace Blue
{
    public class BlueModuleInit : ModuleInit
    {
        public override void Load()
        {
            base.Load();
            BlueForm form = new BlueForm();

            form.Show();
        }
    }
}

- Chúng ta thấy BlueModuleInit kế thừa từ ModuleInit (from metadata):
using System;

namespace Microsoft.Practices.CompositeUI
{
    public abstract class ModuleInit : IModule
    {
        protected ModuleInit();

        public virtual void AddServices();
        public virtual void Load();
    }
}

- Trong ModuleInit khai báo 2 method là AddServiceLoad. Hiện tại file BlueModuleInit chỉ thực hiện override method Load tạo một BlueForm và thực hiện show form này.

Thực hiện tương tự với Red project.

- Trong Shell project add thêm 1 file xml ProfileCatalog.xml tên nội dung như sau:
xml version="1.0" encoding="utf-8" ?>
<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile">
  <Modules>
    <ModuleInfo AssemblyFile="Red.exe" />
    <ModuleInfo AssemblyFile="Blue.exe" />
  Modules>
SolutionProfile>

- ProfileCatalog mô tả các module sẽ thực hiện tìm và load khi application của Shell project thi hành.

- Thực hiện modify Program.cs của project Shell. Nội dung như sau:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI.WinForms;
using Microsoft.Practices.CompositeUI;

namespace Shell
{
    public class Program : FormShellApplication<WorkItem, ShellForm>
    {

        ///
        /// The main entry point for the application.
        ///
        [STAThread]
        static void Main()
        {
            new Program().Run();
        }
    }
}

- Thực hiện modify lại theo các bước bỏ modifier static, thêm kế thừa từ FormShellApplication.

Notes: reference ‘static classes [C#]’ trong MSDN
Static classes and class members are used to create data and functions that can be accessed without creating an instance of the class. Static class members can be used to separate data and behavior that is independent of any object identity: the data and functions do not change regardless of what happens to the object. Static classes can be used when there is no data or behavior in the class that depends on object identity.

- FormShellApplication là một abstract class với generics parameterize TWorkItem là một CompositeUI.WorkItemTShell là một Windows Form
using System;

namespace Microsoft.Practices.CompositeUI.WinForms
{
    public abstract class FormShellApplication<TWorkItem, TShell> : WindowsFormsApplication<TWorkItem, TShell>
        where TWorkItem : Microsoft.Practices.CompositeUI.WorkItem, new()
        where TShell : System.Windows.Forms.Form
    {
        protected FormShellApplication();

        protected override void Start();
    }
}

Note: reference ‘generics [C#]’ trong MSDN:
Generics were added to version 2.0 of the C# language and the common language runtime (CLR). Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.

- Thực hiện build và run Shell vẫn không thấy có chuyện gì xảy ra. Vẫn chỉ có một form Shell hiện lên. Lý do: trong ‘\Shell\bin\Debug’ vẫn chưa có file ProfileCatalog.xml. Chỉnh lại property ‘Copy to Output Directory’ của ProfileCatalog thành ‘Copy Always’.


- Thực hiện build và run lại project sẽ xuất hiện exception như sau:



Lý do là không tìm được Red.exe trong ‘\Shell\bin\Debug\Blue.exe was not found.’

Thực hiện chỉnh lại ‘Output Path’ của các project Red và Blue thành ‘..\Shell\bin\Debug\’ để 3 project cùng output folder.




-         Thực hiện rebuild các solution sẽ thấy kết quả là Shell load cả Red và Blue lên. Để thực hiện được điều này mặc định module loader của CAB đã sử dụng thông tin trong ProfileCatalog. Khi thực hiện close shell thì cả Red form và Blue form cũng sẽ close theo.


-         Nếu tách riêng thì từng project vẫn có thể chạy độc lập với nhau. Trong trường hợp mỗi người thực hiện develop trên một project thì kết quả khi chạy shell sẽ là tổng hợp công việc của 3 developers. Tới đây chúng ta có thể hiểu phần nào ý tưởng của CAB.

Trong phần tiếp theo chúng ta sẽ đề cập đến WorkItem. Một khái niệm căn bản trong CAB.

Introduction to Composite Pattern

-->
Design patterns, CAB & SCSF

Hai phần trước đã nói sơ qua về CAB. Nhưng thật sự thì chưa có gì đọng lại ngoài những cái tên. Tiếp theo chúng ta sẽ điểm qua các Design patterns sử dụng trong CAB và SCSF. Các design patterns không thể liệt kê hết trong phần này mà cần tham khảo trong suốt những phần tiếp theo. Nếu một pattern được sử dụng trong CAB và SCSF cần tham khảo để tiếp tục thì mình sẽ bổ sung. Có thể tham khảo các design patterns tại http://www.dofactory.com/Patterns/Patterns.aspx
Giới thiệu Composite Pattern
 Đầu tiên sẽ đề cập đến design pattern cơ bản là Composite. Có thể tham khảo bài viết tại http://sourcemaking.com/design_patterns/composite.

Composite UML class diagram (sử dụng abstract class)

Composite UML class diagram (sử dụng interface)

Trong UML class diagram có thể thấy 3 phần chính
- Leaf là các đối tượng thành phần (hay còn gọi là Primitive).

- Component là giao diện cho các đối tượng thành phần, khai báo các method căn bản, là giao diện dùng khi access các thành phần.

- Composite định nghĩa các thao tác trên đối tượng thành phần, thực hiện lưu trữ và quản lý các đối tương thành phần.

Composite Pattern cho phép nhóm các object thành một single instance của một object. Các objects được nhóm có tính năng khá tương tự nhau. Object kết quả gọi là composite object. Việc thêm một component vào composite được hình dung tương tự việc thêm vào tree. Về kỹ thuật tất cả các Design patterns đều xoay quanh những tính năng của OOP encapsulation, inheritance, polymorphism...

Trong Composite kỹ thuật chính là polymorphism với 2 cách implement là polymorphism thông qua interface (recommended) và polymorphism qua abstract class.

Lab dành cho Composite Pattern

Ví dụ đơn giản khi có các region:
(England: (London: Chelsea)) và (France: Paris) nhóm thành Europe, một region lớn hơn như sau:

(Europe: (England: (London: Chelsea)) (France: Paris))

-->
Source code với C# (thực hiện qua interface)
Component.cs
-->

using System;
using System.Collections.Generic;
using System.Text;

namespace DesignPatterns
{

    /// <summary>
    ///     Component interface
    /// </summary>
    interface Component
    {

        /// <summary>
        ///     Get the string of component (demo for component operation)
        /// </summary>
        ///
        ///     String of component
        /// </returns>
        string operation();

        /// <summary>
        ///     Get all children
        /// </summary>
        /// <returns>
        ///     List of <see cref="Component"/> children
        /// </returns>
        List<Component> getChildren();

        /// <summary>
        ///     Add to children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to add
        /// </param>
        void add(Component c);

        /// <summary>
        ///     Remove from children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to remove
        /// </param>
        /// <returns>
        ///     A bool value...
        /// </returns>
        bool remove(Component c);
    }
}

Composite.cs
using System;
using System.Collections.Generic;
using System.Text;

namespace DesignPatterns
{

    /// <summary>
    ///     Composite
    /// </summary>
    class Composite : Component
    {

        /// <summary>
        ///     ID
        /// </summary>
        private string _id;

        /// <summary>
        ///     Children collection
        /// </summary>
        private List<Component> _components = new List<Component>();

        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="id" type="string">
        ///    
        /// </param>
        public Composite(string id)
        {
            _id = id;
        }

        #region [ INTERFACE IMPLEMENTATION ]

        /// <summary>
        ///     Operation
        /// </summary>
        /// <returns>
        ///     A string value present name and list of children recursive
        /// </returns>
        public string operation()
        {
            string s = " (" + _id + ":";
            this.getChildren().ForEach
            (
                delegate(Component child)
                {
                    s += " " + child.operation();
                }
            );

            return s + ") ";
        }

        /// <summary>
        ///     Implement interface get all children
        /// </summary>
        /// <returns>
        ///     List of <see cref="Component"/> children
        /// </returns>
        public List<Component> getChildren()
        {
            return _components;
        }

        /// <summary>
        ///     Add to children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to add
        /// </param>
        public void add(Component c)
        {
            _components.Add(c);
        }

        /// <summary>
        ///     Remove from children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to remove
        /// </param>
        /// <returns>
        ///     A bool value...
        /// </returns>
        public bool remove(Component c)
        {
            return _components.Remove(c);
        }

        #endregion

    }
}

Leaf.cs
-->

using System;
using System.Collections.Generic;
using System.Text;

namespace DesignPatterns
{

    /// <summary>
    ///     Leaf
    /// </summary>
    class Leaf : Component
    {

        /// <summary>
        ///    
        /// </summary>
        private string _id;

        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="id" type="string">
        ///     ID
        /// </param>
        public Leaf(string id)
        {
            _id = id;
        }

        #region [ INTERFACE IMPLEMENTATION ]

        /// <summary>
        ///    
        /// </summary>
        /// <returns>
        ///     A string value...
        /// </returns>
        public string operation()
        {
            return _id;
        }

        /// <summary>
        ///     Get all children
        /// </summary>
        /// <returns>
        ///     Just return null
        /// </returns>
        public List<Component> getChildren()
        {
            return null;
        }

        /// <summary>
        ///     Add to children collection, do nothing
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to add
        /// </param>
        public void add(Component c) { }

        /// <summary>
        ///     Remove from children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to remove
        /// </param>
        /// <returns>
        ///     Just return false
        /// </returns>
        public bool remove(Component c) { return false; }

        #endregion

    }
}



Program.cs
-->
using System;
using System.Collections.Generic;
using System.Text;

namespace DesignPatterns
{

    /// <summary>
    ///     Program
    /// </summary>
    class Program
    {

        /// <summary>
        ///     Main entry point
        /// </summary>
        /// <param name="args" type="string[]">
        ///    
        /// </param>
        static void Main(string[] args)
        {

            // England
            Composite england = new Composite("England");

            // London
            Composite london = new Composite("London");
            london.add(new Leaf("Chelsea"));
            england.add(london);

            // Manchester
            Leaf manchester = new Leaf("Manchester");
            england.add(manchester);
            england.remove(manchester);

            // France
            Composite france = new Composite("France");
            france.add(new Leaf("Paris"));

            // Europe
            Composite europe = new Composite("Europe");
            europe.add(england);
            europe.add(france);

            // Get Europe
            Console.WriteLine(europe.operation());
            Console.ReadKey();
        }
    }
}

Source code với C# (thực hiện qua abstract class)
Component.cs
-->

using System;
using System.Collections.Generic;
using System.Text;

namespace DesignPatterns
{

    /// <summary>
    ///     Component interface
    /// </summary>
    public abstract class Component
    {

        /// <summary>
        ///     ID
        /// </summary>
        protected string _id;

        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="id" type="string">
        ///    
        /// </param>
        public Component(string id)
        {
            _id = id;
        }

        /// <summary>
        ///     Get all children
        /// </summary>
        /// <returns>
        ///     List of <see cref="Component"/> children
        /// </returns>
        public abstract List<Component> getChildren();

        /// <summary>
        ///     Get the string of component (demo for component operation)
        /// </summary>
        /// <returns>
        ///     String of component
        /// </returns>
        public abstract string operation();

        /// <summary>
        ///     Add to children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to add
        /// </param>
        public abstract void add(Component c);

        /// <summary>
        ///     Remove from children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to remove
        /// </param>
        /// <returns>
        ///     A bool value...
        /// </returns>
        public abstract bool remove(Component c);
    }
}


Composite.cs
-->
using System;
using System.Collections.Generic;
using System.Text;

namespace DesignPatterns
{

    /// <summary>
    ///     Composite
    /// </summary>
    class Composite : Component
    {

        /// <summary>
        ///     Children collection
        /// </summary>
        private List<Component> _components = new List<Component>();

        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="id" type="string">
        ///    
        /// </param>
        public Composite(string id)
            : base(id)
        { }

        #region [ OVERRIDE METHODS ]

        /// <summary>
        ///     Operation
        /// </summary>
        /// <returns>
        ///     A string value present name and list of children recursive
        /// </returns>
        public override string operation()
        {
            string s = " (" + _id + ":";
            this.getChildren().ForEach(delegate(Component child) { s += " " + child.operation(); });
            return s + ") ";
        }

        /// <summary>
        ///     Implement interface get all children
        /// </summary>
        /// <returns>
        ///     List of <see cref="Component"/> children
        /// </returns>
        public override List<Component> getChildren()
        {
            return _components;
        }

        /// <summary>
        ///     Add to children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to add
        /// </param>
        public override void add(Component c)
        {
            _components.Add(c);
        }

        /// <summary>
        ///     Remove from children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to remove
        /// </param>
        /// <returns>
        ///     A bool value...
        /// </returns>
        public override bool remove(Component c)
        {
            return _components.Remove(c);
        }

        #endregion

    }
}

Leaf.cs
-->
using System;
using System.Collections.Generic;
using System.Text;


namespace DesignPatterns
{

    /// <summary>
    ///     Leaf
    /// </summary>
    class Leaf : Component
    {

        /// <summary>
        ///     Constructor
        /// </summary>
        /// <param name="id" type="string">
        ///     ID
        /// </param>
        public Leaf(string id) : base(id) { }

        #region [ OVERRIDE METHODS ]

        /// <summary>
        ///    
        /// </summary>
        /// <returns>
        ///     A string value...
        /// </returns>
        public override string operation()
        {
            return _id;
        }

        /// <summary>
        ///     Get all children
        /// </summary>
        /// <returns>
        ///     Just return null
        /// </returns>
        public override List<Component> getChildren()
        {
            return null;
        }

        /// <summary>
        ///     Add to children collection, do nothing
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to add
        /// </param>
        public override void add(Component c) { }

        /// <summary>
        ///     Remove from children collection
        /// </summary>
        /// <param name="c" type="Component">
        ///     Child <see cref="Component"/> to remove
        /// </param>
        /// <returns>
        ///     Just return false
        /// </returns>
        public override bool remove(Component c) { return false; }

        #endregion

    }
}

Qua bài này chúng ta sẽ chính thức tìm hiểu CAB. Part tiếp theo sẽ là part đầu tiên và mình sẽ đánh số cho dễ theo dõi bắt đầu là Part 1.