Click here to Skip to main content
Click here to Skip to main content

Components, Aspects, and Dynamic Decorator for Silverlight / WCF Service Applications

, 1 Jun 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Apply components, aspects, and Dynamic Decorator principles to Silverlight / WCF Service applications.

Introduction

As discussed in Components, Aspects, and Dynamic Decorator, application development involves tasks specific to an application type and tasks independent of an application type. This separation of tasks is significant in not only helping you decide on processing strategy, deployment strategy, test strategy, UI strategy, etc., based on specific technologies associated with an application type, but also helping you address the application-type-independent, a.k.a. common, tasks like designing/extending components and addressing cross-cutting concerns systematically. Several guidelines are discussed for development of these common tasks. The Dynamic Decorator is used to address these tasks by following these guidelines. A WinForms application is used as an example.

In this article, I demonstrate how the principles and guidelines are applied to a different application type - Silverlight / WCF Service application - by using the Dynamic Decorator.

Choose Application Type

There are several application types in the .NET world: WinForms, ASP.NET, ASP.NET MVC, and Silverlight. A WinForms application offers rich user interactions and client processing power. An ASP.NET application has advantages of deployment and maintenance via the Web. ASP.NET MVC offers testability plus advantages of ASP.NET. Silverlight applications offer a rich user interface over the Web. Different types of applications have different technology specifics, which may or may not meet your requirements. You should choose your application type based on your processing strategy, deployment strategy, maintenance strategy, test strategy, UI strategy, etc.

Different application types also have different application programming models, UI elements, state management, event processing models, etc. You need to address tasks related to an application type once you select the application type for your application.

Address Common Tasks

In addition to the tasks specific to an application type, there are also tasks independent of a particular application type in application development. For instance, regardless of application types, you face tasks like designing, extending components, and addressing cross-cutting concerns. They are common for all application types.

A set of principles are given for the development of these common tasks in the article Components, Aspects, and Dynamic Decorator. They are listed here again as follows:

  • Design components to meet business requirements in a general way
  • Design aspects as global methods in their own modules
  • Add aspects to objects as needed
  • Extend objects as needed

Please refer to Components, Aspects, and Dynamic Decorator for details of the discussion about these principles. You may also need to read the article Dynamic Decorator Pattern to understand the Dynamic Decorator, and read the article Add Aspects to Object Using Dynamic Decorator to understand aspects programming using Dynamic Decorator.

Example

In the following sections, an example application is discussed to demonstrate how the above principles are applied to a Silverlight / WCF Service application by using the Dynamic Decorator.

The same problem discussed in Components, Aspects, and Dynamic Decorator as an example is used here. This time, we choose Silverlight / WCF Service application as the application type instead of the WinForms application. For convenience, I state the problem again as follows.

Problem

Display employees based on selection of a department.

Components

Assume there are two components, Employee and Department. For Employee, there is a corresponding RepositoryEmployee component to contain a collection of objects of Employee. For Department, there is a corresponding RepositoryDepartment component to contain a collection of objects of Department. The code of these components is listed as follows.

public interface IEmployee
{
    System.Int32? EmployeeID { get; set; }
    System.String FirstName { get; set; }
    System.String LastName { get; set; }
    System.DateTime DateOfBirth { get; set; }
    System.Int32? DepartmentID { get; set; }
    System.String FullName();
    System.Single Salary();
}

public class Employee : IEmployee
{
    #region Properties

    public System.Int32? EmployeeID { get; set; }
    public System.String FirstName { get; set; }
    public System.String LastName { get; set; }
    public System.DateTime DateOfBirth { get; set; }
    public System.Int32? DepartmentID { get; set; }

    #endregion

    public Employee(
        System.Int32? employeeid
        , System.String firstname
        , System.String lastname
        , System.DateTime bDay
        , System.Int32? departmentID
    )
    {
        this.EmployeeID = employeeid;
        this.FirstName = firstname;
        this.LastName = lastname;
        this.DateOfBirth = bDay;
        this.DepartmentID = departmentID;
    }

    public Employee() { }

    public System.String FullName()
    {
        System.String s = FirstName + " " + LastName;
        return s;
    }

    public System.Single Salary()
    {
        System.Single i = 10000.12f;
        return i;
    }
}

public interface IDepartment
{
    System.Int32? DepartmentID { get; set; }
    System.String Name { get; set; }
}

public class Department : IDepartment
{
    #region Properties

    public System.Int32? DepartmentID { get; set; }
    public System.String Name { get; set; }

    #endregion

    public Department(
        System.Int32? departmentid
        , System.String name
    )
    {
        this.DepartmentID = departmentid;
        this.Name = name;
    }

    public Department() { }
}

public interface IRepository<T>
{
    List<T> RepList { get; set; }
    void GetAll();
}

public class RepositoryEmployee : IRepository<IEmployee>
{
    private List<IEmployee> myList = null;

    public List<IEmployee> RepList
    {
        get { return myList; }
        set { myList = value; }
    }

    public RepositoryEmployee()
    {
    }

    public void GetAll()
    {
        myList = new List<IEmployee> { new Employee(1, "John", "Smith", new DateTime(1990, 4, 1), 1), 
            new Employee(2, "Gustavo", "Achong", new DateTime(1980, 8, 1), 1), 
            new Employee(3, "Maxwell", "Becker", new DateTime(1966, 12, 24), 2), 
            new Employee(4, "Catherine", "Johnston", new DateTime(1977, 4, 12), 2), 
            new Employee(5, "Payton", "Castellucio", new DateTime(1959, 4, 21), 3), 
            new Employee(6, "Pamela", "Lee", new DateTime(1978, 9, 16), 4) };
    }
}

public class RepositoryDepartment : IRepository<IDepartment>
{
    private List<IDepartment> myList = null;

    public List<IDepartment> RepList
    {
        get { return myList; }
        set { myList = value; }
    }

    public RepositoryDepartment()
    {
    }

    public void GetAll()
    {
        myList = new List<IDepartment> { new Department(1, "Engineering"), 
            new Department(2, "Sales"), 
            new Department(3, "Marketing"), 
            new Department(4, "Executive") };
    }
}

In this application, the data for employees and departments is hard-coded in two lists to simplify our discussion. In a real world application, these data are normally persisted in a relational database. Then, you will need to create a data layer to retrieve them and put them in the lists.

It is worth noting that the employee list is populated by the inserting order without any kind of sorting in place. It is difficult to anticipate what kinds of sorting to support for this component at this time. In applications, an object of the component may need to sort by last name, another may need to sort by birth day, a third may not need to sort at all. So, it is better to defer the implementation of sorting until the component is used in an application. By designing the component RepositoryEmployee without concern about sorting, the Principle "Design components to meet business requirements in a general way" is followed. This way, the component is stable and closed.

HRSilverlight

HRSilverlight is a Silverlight application which uses the above components to display employees based on selection of a department. Since it is a Silverlight application, it follows Silverlight's application programming model and event model. The Silverlight code is listed as follows.

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();

        HRServiceClient proxy = new HRServiceClient();
        proxy.DepartmentAllCompleted += 
          new EventHandler<DepartmentAllCompletedEventArgs>(proxy_DepartmentAllCompleted);
        proxy.EmployeeAllCompleted += 
          new EventHandler<EmployeeAllCompletedEventArgs>(proxy_EmployeeAllCompleted);

        proxy.DepartmentAllAsync();
        proxy.EmployeeAllAsync();
    }

    void proxy_DepartmentAllCompleted(object sender, DepartmentAllCompletedEventArgs e)
    {
        comboBox1.DataContext = e.Result;
    }

    void proxy_EmployeeAllCompleted(object sender, EmployeeAllCompletedEventArgs e)
    {
        dataGrid1.DataContext = e.Result;
    }

    private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        Department d = comboBox1.SelectedValue as Department;

        HRServiceClient proxy = new HRServiceClient();
        proxy.EmployeeByDepartmentIDAsync(d);
        proxy.EmployeeByDepartmentIDCompleted += new 
          EventHandler<EmployeeByDepartmentIDCompletedEventArgs>(
          proxy_EmployeeByDepartmentIDCompleted);
    }

    void proxy_EmployeeByDepartmentIDCompleted(object sender, 
         EmployeeByDepartmentIDCompletedEventArgs e)
    {
        dataGrid1.DataContext = e.Result;
    }
}

The above code is executed at client side in a device, e.g., a browser. Most of the business logic is wrapped in the server side as a WCF service, HRService, which is consumed by the Silverlight code via a client stub, HRServiceClient.

The code for HRService is listed as follows.

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = 
       AspNetCompatibilityRequirementsMode.Allowed)]
public class HRService
{
    private static int iStaticDep = 0;

    [OperationContract]
    public List<Department> DepartmentAll()
    {
        IRepository<IDepartment> rpDepartment = null;
        rpDepartment = new RepositoryDepartment();
        rpDepartment.GetAll();

        List<Department> depObj = new List<Department>();
        foreach (IDepartment i in rpDepartment.RepList)
        {
            depObj.Add((Department)i);
        }
        return depObj;
    }

    [OperationContract]
    public List<Employee> EmployeeAll()
    {
        IRepository<IEmployee> rpEmployee = null;
        rpEmployee = new RepositoryEmployee();
        rpEmployee.GetAll();

        List<Employee> empObj = new List<Employee>();
        foreach (IEmployee i in rpEmployee.RepList)
        {
            empObj.Add((Employee)i);
        }
        return empObj;
    }

    [OperationContract]
    public List<Employee> EmployeeByDepartmentID(Department dep)
    {
        IDepartment iDep = (IDepartment)dep;
        iStaticDep = iDep.DepartmentID.Value;

        IRepository<IEmployee> rpEmployee = null;
        rpEmployee = new RepositoryEmployee();
        rpEmployee.GetAll();

        List<IEmployee> empSel = null;
        if (rpEmployee.RepList != null)
        {
            empSel = rpEmployee.RepList.FindAll(
                (IEmployee emp) => { return emp.DepartmentID.Value == iStaticDep; });
        }

        List<Employee> empObj = new List<Employee>();
        foreach (IEmployee i in empSel)
        {
            empObj.Add((Employee)i);
        }
        return empObj;
    }
}

One thing to notice is that the methods return class type lists List<Department> or List<Employee> instead of interface type lists List<IDepartment> or List<IEmployee>. The reason for this is that WCF does serialization by contract not by type, therefore does not know how to serialize interface types like IEmployee and IDepartment.

When it runs, all employees are displayed as follows.

When a department is selected, the employees for that department are displayed.

Sort Employee List

Now, let's say you want the object rpEmployee, an instance of the RepositoryEmployee component, to have functionality of sorting employees by last name. Here is what you need to do in the HRService.

First, you create a comparer class for the sorting as follows.

internal class EmployeeLastnameComparer : IComparer<IEmployee>
{
    public int Compare(IEmployee e1, IEmployee e2)
    {
        return String.Compare(e1.LastName, e2.LastName);
    }
}

Then, call the Dynamic Decorator just before the rpEmployee.GetAll() in the method EmployeeAll as follows.

rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
    rpEmployee,
    new String[] { "GetAll" },
    null,
    new Decoration((x, y) =>
    {
        object target = x.Target;
        if (target.GetType().ToString() == "ThirdPartyHR.RepositoryEmployee")
        {
            List<IEmployee> emps = ((IRepository<IEmployee>)target).RepList;
            IEnumerable<IEmployee> query = emps.OrderByDescending(emp => emp,
                new EmployeeLastnameComparer()).ToList<IEmployee>();
            ((IRepository<IEmployee>)target).RepList = (List<IEmployee>)query;
        }
    }, null));

That's it. Now, your HRSilverlight displays employees sorted by their last names. Build it and run it. You will see that the employees are displayed and ordered by their last names as follows.

When you select a department, the employees associated with this department are displayed and ordered by their last names.

Note that a Lambda expression is used to provide an anonymous method for this employee repository object to add sorting capability. Of course, you can use a normal method for the sorting logic. However, since this sorting logic is particularly for the employee repository object rpEmployee and not shared by other objects, it is more concise to keep it in an anonymous method.

There are a few points worth noting here. First, the Principle "Extend objects as needed" is followed. When we designed the component RepositoryEmployee, the sorting requirements were not clear yet. By the time we used the object rpEmployee in the application, it was clear that we need to sort the employee list by employee's last name. So, we extended this object to sort employee list by employee's last name. Second, the sorting capability was attached to the object rpEmployee without modifying its component or deriving from it. Third, the object rpEmployee is the one and the only one instance of the RepositoryEmployee component which has sorting functionality, independently of other instances created by RepositoryEmployee.

Design Aspects

Say, you want your HRSilverlight application to address cross-cutting concerns of entering/exiting logging and security checking. By following the Principle "Design aspects as global methods in their own modules", these aspects are put in one class SysConcerns as individual public methods and packed in their own module. The following is the code for these concerns.

public class SysConcerns
{
    public static void EnterLog(AspectContext ctx, object[] parameters)
    {
        StackTrace st = new StackTrace(new StackFrame(4, true));
        Console.Write(st.ToString());
            
        IMethodCallMessage method = ctx.CallCtx;
        string str = "Entering " + ctx.Target.GetType().ToString() + "." + 
                     method.MethodName + "(";
        int i = 0;
        foreach (object o in method.Args)
        {
            if (i > 0)
                str = str + ", ";
            str = str + o.ToString();
        }
        str = str + ")";

        Console.WriteLine(str);
        Console.Out.Flush();
    }

    public static void ExitLog(AspectContext ctx, object[] parameters)
    {
        IMethodCallMessage method = ctx.CallCtx;
        string str = "Exiting " + ctx.Target.GetType().ToString() + "." + 
                     method.MethodName + "(";
        int i = 0;
        foreach (object o in method.Args)
        {
            if (i > 0)
                str = str + ", ";
            str = str + o.ToString();
        }
        str = str + ")";

        Console.WriteLine(str);
        Console.Out.Flush();
    }

    public static void AdminCheck(AspectContext ctx, object[] parameters)
    {
        Console.WriteLine("Has right to call");
        return;
    }
}

The EnterLog writes entering logs while ExitLog writes exiting logs. The AdminCheck writes a log and returns.

You may need to modify these methods based on your system requirements. You can also enhance them by accessing various information in the context, the target, and the input parameters. To see how the context, target, and parameters are used to enhance your aspects, please refer to Add Aspects to Object Using Dynamic Decorator.

Use Aspects

With aspects defined, you can add them to objects as needed in the application.

Say you want to add the security checking aspect before calling the GetAll method of the repository object rpDepartment of the component RepositoryDepartment. You also want to add the entering log and exiting log to the same object. You add the following code right before the rpDepartment.GetAll() in the method DepartmentAll.

rpDepartment = (IRepository<IDepartment>)ObjectProxyFactory.CreateProxy(
    rpDepartment,
    new String[] { "GetAll" },
    new Decoration(new DecorationDelegate(SysConcerns.AdminCheck), 
                   new object[] { Thread.CurrentPrincipal }),
    null);

rpDepartment = (IRepository<IDepartment>)ObjectProxyFactory.CreateProxy(
    rpDepartment,
    new String[] { "GetAll" },
    new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
    new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));

Then, assume you want to add entering log and exiting log to the GetAll method of the object rpEmployee of the RepositoryEmployee component, just insert the following code before rpEmployee.GetAll() in the method EmployeeAll.

rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
    rpEmployee,
    new String[] { "GetAll" },
    new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
    new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));

Finally, assume you want to track which department is accessed; you can add the following code in the method EmployeeByDepartmentID just before using the department ID property of the selected object iDep of the Department component iStaticDep = iDep.DepartmentID.Value.

iDep = (IDepartment)ObjectProxyFactory.CreateProxy(
    iDep,
    new String[] { "get_DepartmentID" },
    new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
    null);

Now, the cross-cutting concerns are addressed for the HRSilverlight application.

Note that the aspects are added to the objects when needed. There is no change in the component classes. And only the objects decorated with Dynamic Decorator have the aspects, independent of other objects of the component classes. Also, an aspect can be applied to different objects, of either the same type or different types. For example, the SysConcerns.EnterLog is used for rpDepartment (an object of RepositoryDepartment), rpEmployee (an object of RepositoryEmployee), and dpSel (an object of Department).

HRSilverlight Extended

Since all the new functionality (sorting and aspects) is added to the server side in the WCF service, there is no change for the Silverlight code in the client side.

For your convenience, the code of HRService after extended is listed as follows.

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = 
       AspNetCompatibilityRequirementsMode.Allowed)]
public class HRService
{
    internal class EmployeeAgeComparer : IComparer<IEmployee>
    {
        public int Compare(IEmployee e1, IEmployee e2)
        {
            return DateTime.Compare(e1.DateOfBirth, e2.DateOfBirth);
        }
    }

    internal class EmployeeLastnameComparer : IComparer<IEmployee>
    {
        public int Compare(IEmployee e1, IEmployee e2)
        {
            return String.Compare(e1.LastName, e2.LastName);
        }
    }

    private static int iStaticDep = 0;

    [OperationContract]
    public List<Department> DepartmentAll()
    {
        IRepository<IDepartment> rpDepartment = null;
        rpDepartment = new RepositoryDepartment();

        rpDepartment = (IRepository<IDepartment>)ObjectProxyFactory.CreateProxy(
            rpDepartment,
            new String[] { "GetAll" },
            new Decoration(new DecorationDelegate(SysConcerns.AdminCheck), 
                           new object[] { Thread.CurrentPrincipal }),
            null);

        rpDepartment = (IRepository<IDepartment>)ObjectProxyFactory.CreateProxy(
            rpDepartment,
            new String[] { "GetAll" },
            new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
            new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));

        rpDepartment.GetAll();

        List<Department> depObj = new List<Department>();
        foreach (IDepartment i in rpDepartment.RepList)
        {
            depObj.Add((Department)i);
        }
        return depObj;
    }

    [OperationContract]
    public List<Employee> EmployeeAll()
    {
        IRepository<IEmployee> rpEmployee = null;
        rpEmployee = new RepositoryEmployee();

        rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
            rpEmployee,
            new String[] { "GetAll" },
            null,
            new Decoration((x, y) =>
            {
                object target = x.Target;
                if (target.GetType().ToString() == "ThirdPartyHR.RepositoryEmployee")
                {
                    List<IEmployee> emps = ((IRepository<IEmployee>)target).RepList;
                    IEnumerable<IEmployee> query = emps.OrderByDescending(emp => emp,
                        new EmployeeLastnameComparer()).ToList<IEmployee>();
                    ((IRepository<IEmployee>)target).RepList = (List<IEmployee>)query;
                }
            }, null));

        //Add entering log to employee list
        rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
            rpEmployee,
            new String[] { "GetAll" },
            new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
            new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));

        rpEmployee.GetAll();

        List<Employee> empObj = new List<Employee>();
        foreach (IEmployee i in rpEmployee.RepList)
        {
            empObj.Add((Employee)i);
        }
        return empObj;
    }

    [OperationContract]
    public List<Employee> EmployeeByDepartmentID(Department dep)
    {
        IDepartment iDep = (IDepartment)dep;
        iDep = (IDepartment)ObjectProxyFactory.CreateProxy(
            iDep,
            new String[] { "get_DepartmentID" },
            new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
            null);

        iStaticDep = iDep.DepartmentID.Value;

        IRepository<IEmployee> rpEmployee = null;
        rpEmployee = new RepositoryEmployee();

        rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
            rpEmployee,
            new String[] { "GetAll" },
            null,
            new Decoration((x, y) =>
            {
                object target = x.Target;
                if (target.GetType().ToString() == "ThirdPartyHR.RepositoryEmployee")
                {
                    List<IEmployee> emps = ((IRepository<IEmployee>)target).RepList;
                    IEnumerable<IEmployee> query = emps.OrderByDescending(emp => emp,
                        new EmployeeLastnameComparer()).ToList<IEmployee>();
                    ((IRepository<IEmployee>)target).RepList = (List<IEmployee>)query;
                }
            }, null));

        rpEmployee.GetAll();

        List<IEmployee> empSel = null;
        if (rpEmployee.RepList != null)
        {
            empSel = rpEmployee.RepList.FindAll(
                (IEmployee emp) => { return emp.DepartmentID.Value == iStaticDep; });
        }

        List<Employee> empObj = new List<Employee>();
        foreach (IEmployee i in empSel)
        {
            empObj.Add((Employee)i);
        }
        return empObj;
    }
}

One thing to notice is that the object returned by ObjectProxyFactory.CreateProxy is assigned back to the variable originally pointed to the target. For example, rpEmployee originally was assigned an object of RepositoryEmployee - the target. After calling ObjectProxyFactory.CreateProxy, it is assigned the returned object, which is a proxy of the target. It is subtle but important. The returned object of ObjectProxyFactory.CreateProxy is a proxy of the target. By using the same variable for both the target and its proxy, the original code is in tact. That means that the target and its proxy are interchangeable. If the variable is pointed to the target, the target is used as is. If the variable is pointed to the proxy of the target, additional functionality is executed before or after the target is used. Actually, if you remove all the code that calls ObjectProxyFactory.CreateProxy, you get the original code before you extended the object and added the aspects to objects.

Last, before running the application, you need to modify the Global.asax method to redirect console output to a file hrlog.txt. These modifications are for this application only since the entering/exiting logging aspects use the console. Your application may use a different logging mechanism. In that case, you may need to make the corresponding changes. The modified application class is listed as follows.

void Application_Start(object sender, EventArgs e) 
{
    FileStream fileStream = null;
    
    string path = Path.GetDirectoryName(Server.MapPath("~"));
    if (!File.Exists(path + "\\hrlog.txt"))
    {
        fileStream = new FileStream(path + "\\hrlog.txt", FileMode.Create);
    }
    else
        fileStream = new FileStream(path + "\\hrlog.txt", FileMode.Truncate);

    TextWriter tmp = Console.Out;
    Application["origOut"] = tmp;

    StreamWriter sw1 = new StreamWriter(fileStream);
    Console.SetOut(sw1);

    Application["logStream"] = sw1;
}
    
void Application_End(object sender, EventArgs e) 
{
    TextWriter origStrm = (TextWriter)Application["origOut"];
    Console.SetOut(origStrm);

    StreamWriter tmp = (StreamWriter)Application["logStream"];
    Stream fileStream = tmp.BaseStream;

    tmp.Close();
    fileStream.Close();
}

When the application runs, you will see the following output in the file hrlog.txt.

   at HRSilverlight.Web.HRServiceExtended.EmployeeAll() in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 94
Entering ThirdPartyHR.RepositoryEmployee.GetAll()
Exiting ThirdPartyHR.RepositoryEmployee.GetAll()
   at HRSilverlight.Web.HRServiceExtended.DepartmentAll() in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 55
Entering ThirdPartyHR.RepositoryDepartment.GetAll()
Has right to call
Exiting ThirdPartyHR.RepositoryDepartment.GetAll()
   at HRSilverlight.Web.HRServiceExtended.EmployeeByDepartmentID(Department dep) in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 114
Entering ThirdPartyHR.Department.get_DepartmentID()
   at HRSilverlight.Web.HRServiceExtended.DepartmentAll() in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 55
Entering ThirdPartyHR.RepositoryDepartment.GetAll()
Has right to call
Exiting ThirdPartyHR.RepositoryDepartment.GetAll()
   at HRSilverlight.Web.HRServiceExtended.EmployeeAll() in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 94
Entering ThirdPartyHR.RepositoryEmployee.GetAll()
Exiting ThirdPartyHR.RepositoryEmployee.GetAll()
   at HRSilverlight.Web.HRServiceExtended.EmployeeByDepartmentID(Department dep) in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 114
Entering ThirdPartyHR.Department.get_DepartmentID()
   at HRSilverlight.Web.HRServiceExtended.EmployeeByDepartmentID(Department dep) in 
   C:\CBDDynDecoratorSilverlight\HRSilverlight.Web\HRServiceExtended.svc.cs:line 114
Entering ThirdPartyHR.Department.get_DepartmentID()

Points of Interest

The philosophy of "Add aspects to objects as needed" and "Extend objects as needed" is applied to Silverlight / WCF Service applications. Silverlight / WCF Service developers may find the following principles useful for addressing common tasks with the Dynamic Decorator.

  • Design components to meet business requirements in a general way
  • Design aspects as global methods in their own modules
  • Add aspects to objects as needed
  • Extend objects as needed

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gary H Guo

United States United States
Object-oriented (OO) is about "classes" not "objects". But I truly believe that "objects" deserve more our attentions. If you agree, read more on... Dynamic Object Programming (DOP), Component-Based Object Extender (CBO Extender), AOP Container and Dynamic Decorator Pattern.
 
Mobile development is not just another type of front end. The real challenge is actually in the back end: How to present meaningful information in time to mobile users with exponentially increased data flooding around? Here is my first mobile solution: SmartBars - Barcode Reader, Price Comparison and Coupons.
 
Gary lives in southeast Michigan. My first programming language is FORTRAN. For the last a few years, I have primarily focused on .NET technologies with Mobile Development as my newest interest.

Comments and Discussions

 
Questionnice article Pinmembersana loukil12-May-13 1:16 
AnswerRe: nice article PinmemberGary H Guo12-Jun-13 9:28 
GeneralMy vote of 5 PinmemberFilip D'haene1-Jun-11 12:33 
GeneralRe: My vote of 5 PinmemberGary H Guo2-Jun-11 8:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 1 Jun 2011
Article Copyright 2011 by Gary H Guo
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid