Adapter

The Adapter activity allows you to specify masking rules for adapted values.

In other words, it allows you to adapt certain types to use a masking algorithm made for a different type. This activity is often used to adapt various similar record types (e.g. different types that represent a person) to a general type (one generalizing a Person type) so it can use a standardized custom masking. With proper planning, this activity can save a lot of time and work by allowing you to create complex masking for a certain type only once and then adapting various values to use it, as opposed to recreating the entire masking every time it is needed.

Table of contents
Usage
Example
Properties

Usage

To use the Adapter masking activity, it must be placed inside a masking iterator activity which has to be placed inside a masking engine. Once placed, a Select Types window will open where you must choose the TAdapted type, i.e. the type to which you will convert/adapt your records to. Selecting the Browse for Types... option will open the Browse and Select a .Net Type window where you can search for more types. After clicking OK, you can choose the property you want to mask. Next, enter a new instance of a generator to generate values.

Select Types window Figure 1: Select Types window

Next, you must set the Conversion property as an expression that will adapt your record to the chosen type. This is mostly done by using custom code. Note that you have to pass custom lambda expressions to the adapted class that can access and change the values in the original record so they can be written to the database after masking. Lastly, you must define the masking algorithm you will use to mask the adapted value and with it the original value.

Example

Let's assume that we are masking a large database that has many tables describing people (customers, business representatives, etc.). Many of these tables save the first name, last name and the country of the people in question. We can take advantage of this by creating a custom masking that masks these values instead of creating the same masking for every table. To do that we will need to create a new type that will contain only first name, last name and country, a custom masking for that type and then adapt every mentioned applicable type so it can use this masking.

The first step will be to create a new type (e.g. IPerson) that contains the properties various tables describing people share (e.g. first name, last name, country, etc.). To do this we add a new interface IPerson with properties FirstName, LastName and Country to our custom code. The interface looks like this:

public interface IPerson
{
    string FirstName { get; set; }
    string LastName { get; set; }
    string Country { get; set; }
}

After building the project, we can create a new custom masking and choose the new IPerson interface as the type. We complete the empty masking with a way to mask first names, last names and countries.

Custom Person masking Figure 2: Custom Person masking

We can then proceed with creating defining the masking algorithm for the Customer table. First we place the Customer masking activity in the main workflow and put the Adapter activity within it. In the Select Types window, we choose the Browse for Types... option where we can search and select the IPerson type we created earlier. We now have to create some more custom code that will handle the conversion from the database record type to IPerson. This new class must have properties that save the first name, last name and country values of the original record so we can use them during masking, but it also has to change the original record through Action properties whenever these values change. This class looks like this:

public class CustomerController : IPerson
{
    private string firstName;
    private string lastName;
    private string country;
    private Action<string> writeFirstName;
    private Action<string> writeLastName;
    private Action<string> writeCountry;

    public string FirstName
    {
        get
        {
            return firstName;
        }
        set
        {
            firstName = value;
            writeFirstName?.Invoke(value);
        }
    }

    public string LastName
    {
        get
        {
            return lastName;
        }
        set
        {
            lastName = value;
            writeLastName?.Invoke(value);
        }
    }

    public string Country
    {
        get
        {
            return country;
        }
        set
        {
            country = value;
            writeCountry?.Invoke(value);
        }
    }

    public CustomerController(Customer record, Action<string> writeFirstName, Action<string> writeLastName, Action<string> writeCountry)
    {
        this.firstName = record.FirstName;
        this.lastName = record.LastName;
        this.Country = record.Country;
        this.writeFirstName = writeFirstName;
        this.writeLastName = writeLastName;
        this.writeCountry = writeCountry;
    }
}

The CustomerController class implements the IPerson interface so it can use the custom Person masking created before, i.e. this class acts as a "middle-man" between the original record and the custom Person masking We save the original record properties inside the constructor so that we can use them during masking, and we can use getters to access them through the properties of this class. Furthermore, every time the values of these properties are changed, we use Actions to update the original record as well. Next, we need to create a method that will take the original record and convert it to the CustomerController class, thus adapting it to use the custom Person masking made for the IPerson type. We can place this method inside the default Code class so we can use it in our workflow. This method looks like this:

public static Func<Customer, IPerson> ConvertCustomerToPerson()
{
    return (record) => new CustomerController(record, (value) => record.FirstName = value, 
                                                        (value) => record.LastName = value, 
                                                        (value) => record.Country = value);
}

What this method does is takes the original record of the Customer type and converts it to the IPerson type. Note the way we are sending lambda expressions as Actions so we can access the original record from within the adapted value. We can now go back to the workflow and put Code.ConvertCustomerToPerson() as the Conversion property in the Adapter activity. Lastly, we place the custom PersonMasking activity within it.

Adapter example Figure 3: Adapting Customer values and masking them in the custom PersonMasking masking activity

Properties

Property group Property name Description Example
Misc Conversion Expression that adapts the original record to the chosen type. Code.ConvertCustomerToPerson()
DisplayName Display name of the activity in the workflow. Adapter
MaskingForAdapted Masking algorithm for the adapted values. The property is set in XAML/workflow designer and it should be ignored here. -
Result Contains the masking definition object. It's a part of the masking infrastructure and should be ignored. -