Saturday, April 18, 2015

OpenLink Findur OpenComponents External Plugin

At work we've been trying for the last 2 years to replace our current system with OpenLink Findur. I was involved in integrating our internal pricing library by using their OpenComponents .NET API. It was tough but we successfully delivered that part of the project, so I was out of the project and had been doing other things since then. Around a month ago, I was suddenly asked to take over yet another part of this massive project from a contractor hired to do this as the management didn't want to extend her contract. So that's how I got sucked into this project again T_T.

The part I was asked to take over is a .NET C# Console Application built using OpenLink OpenComponents .NET API. This plugin is attached to Findur End of Day (EOD) workflow and runs at the very end of the workflow. This plugin extracts some of the EOD results and produces xml's needed by a lot of our legacy tools that we don't have time yet to adjust to communicate with Findur. At least not at this phase of the project.

If you're like me, I find debugging extremely important especially when you're pulled out of whatever you're doing, thrown into a massive project and expected to be up and running after 2 hours handover. Well the bad news was that she said it's impossible to debug the plugin. I asked her how she managed to build something quite big (there're more than 5000 lines of codes) without debugging it. She said she put logging lines almost everywhere, pressed F5 and waited until it either crashed (and then checked the log) or finished executing (and then checked the resulting xml's). My first reaction was: Are you kidding me?!? She also said that this plugin could only work when there's only one Findur session running in the application server as users access Findur via Citrix. At this point I had to pinch myself to check whether I was dreaming. Surely something is wrong when you need an extra application server wholly dedicated to test this plugin. It just sounds absurd.

Okay now that I'm done rambling, let's formulate the problems she mentioned and what I did to somehow make it works.

Problem #1: "You cannot debug this external Findur plugin..."


SEHException - External component has thrown an exception

So first thing I did after having all my development environment set up was putting a random breakpoint in the solution and pressing F5. It ran for a couple of seconds and then it stopped at that breakpoint. Hmmm, I thought she said we couldn't debug it? So I pressed F5 again to resume and bang! it threw a 'SEHException - External component has thrown an exception' in:

   Application app = Application.GetInstance();
Ah so this was what she meant by "You cannot debug this external Findur plugin..."

Next thing I did was going to the guy who's responsible for deploying this plugin to a test environment and asked him whether it had ever worked before and how he'd run it. He said it had worked before. At the end of Findur EOD workflow, there's a process that would trigger this plugin. He even demonstrated that it worked by manually double clicking the .exe file. I went back to my desk and double clicked the .exe file in Visual Studio bin\Debug folder. It ran successfully and produced the expected xml's. So it did seem that the contractor was right, we can't debug this plugin.

Being the person that I am, I didn't give up. To be honest I didn't want to take on a project where I'd need to code blindly and invest my time in writing log entries. Besides I wasn't even sure that log entries would help me with coding the logic of extracting data from Findur simulation results. We're talking about lots and lots of Findur Table class inquiries here. I couldn't find anything about this exception on google, nothing related to Findur, so I tried the next best thing: cleaning the solution. It had done the trick in multiple occasions in the past and I was desperate, so why not? And by "cleaning" I meant manually deleting the output files, not using Visual Studio Clean Solution. It refused to delete vshost.exe file as it's in use, obviously because I had the solution still open. Then I thought, "Wait a minute, vshost.exe is a feature in Visual Studio related to debugging and my current problem is related to debugging. Running it by double clicking the .exe works fine." At this point, I just followed my gut instinct, I closed the solution, deleted the vshost.exe, reopened the solution, recompiled and pressed F5. And then the magic happened, it didn't crash on Application.GetInstance(). Hooray!!


Debug stop working after a couple of step-into's

Apparently it's not over yet. Debugging only lasted for approximately 3 step-into's and then it simply refused to stop on any more breakpoint. The yellow line highlighting the current line went away, but it never reached the next line. The solution window title showed "(Running)" and I couldn't do anything but stopped the debugging session and restarted the whole thing. I ended up having to choose my breakpoint very wisely since I only got a limited number of step-into's. Was this what the contractor meant by "You cannot debug this external Findur plugin..."?

Again being the person that I am, I refused to give up. "Choose your breakpoints wisely" lol it even sounds silly. So going back to google search, some people recommended switching off property evaluation and other implicit function calls. This setting can be found in Tools > Options and then select Debuging > General. Find check box Enable property evaluation and other implicit function calls and uncheck it. This, my friends... didn't work.

This worked for me though:
Tools > Import and Export Settings > Reset all settings
If you're reading this because you have the same problem and about to try it out, please please note that you're going to LOSE your Resharper settings (if you're using it) and personalized keyboard shortcuts! So make sure you back up your settings when it asks you to.



Problem #2: "External plugin can only work if there's only one Findur session running..."

Original code:

   Application app = Application.GetInstance();
   Session session = app.Attach();
Application and Session class can be found in Olf.Openrisk.Application namespace. Here we see that we don't tell it to attach to a specific session. No wonder it only worked if there's only one session running. It'll just attach itself to a random session.

Now I want the plugin to attach to a specific Findur database session. If that session is not running, I want the plugin to start it and then attach itself to it. For this purpose, I introduce 3 configuration keys in App.config:

    <add key="DatabaseName" value="Findur_Test" />
    <add key="OlfBinFolder" value="C:\OpenLink\Findur_V14_1_01172015MR_02182015_1037\bin" />
    <add key="OlfParams" value="-olfcfg \\servername\ConfigFiles\TestConfig.olf -u svcAccUser -p 1234567" />
DatabaseName is the name of Findur database session we want to attach our plugin to. OlfBinFolder specifies the location where Findur is installed. There should be an executable master.exe in this bin folder which is the one we use to start Findur. OlfParams specifies the command parameters of master.exe. When you run Findur from the command prompt it reads as follows:
  C:\OpenLink\Findur_V14_1_01172015MR_02182015_1037\bin\master.exe -olfcfg \\servername\ConfigFiles\TestConfig.olf -u svcAccUser -p 1234567

Findur Application class has another Attach method where we can specify a session ID we want to attach to. There can only be one Application instance (because it's a singleton), while we can have multiple Findur sessions running on it.

If we have a Findur session running, we can see the session ID by clicking on the i (information) icon. Unfortunately this session ID changes every time we start a session and there's no way the plugin can access this information without first attaching itself to a running session, so we cannot know beforehand what the session ID is (classic chicken and egg problem isn't it? ;)). Fortunately there's SessionDescriptions property on the Application class which returns all the session ID's running on it. We can iterate through each of these session ID's, attach our plugin to it so that we can ask for the session's database name. When the attached database name is not equal to the database name we want ie. the one we specified in App.config, we detach it and continue with the next session ID and repeat the process until we find the database we want. If we don't find it, return null. Null means that the session we want to attach to is not running and therefore we will start it by running whatever specified in OlfBinFolder and OlfParams.

The adjusted code:

        Application app = Application.GetInstance();
        Session session = GetSession(app);
where:
        private Session GetSession(Application app)
        {     
            string databaseName = ConfigurationManager.AppSettings["DatabaseName"];
            Session session = GetRunningSession(app, databaseName);
            if (session == null)
            {
                string binDir = ConfigurationManager.AppSettings["OlfBinFolder"];
                string parameters = ConfigurationManager.AppSettings["OlfParams"];
                session = app.StartSession(binDir, parameters);
            }

            return session;
        }

        private Session GetRunningSession(Application app, string databaseName)
        {
            IEnumerable<int> sessionIds = app.SessionDescriptions.Select(x => x.Id);
            foreach (int sessionId in sessionIds)
            {
                Session attachedSession = app.Attach(sessionId);
                string attachedDatabaseName = attachedSession.DatabaseName;
                if (attachedDatabaseName != databaseName)
                {
                    app.Detach();
                }
                else
                {
                    return attachedSession;
                }
            }

            return null;
        }

So what is the moral of the story?
* Don't blindly trust what you're told, what does the code say?
* Don't give up and google is your friend ;)

Sunday, April 12, 2015

Annoying Last Empty Row on WPF DataGrid



<Window x:Class="AnnoyingDataGridExtraRow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Name="ThisWindow">

    <DataGrid ItemsSource="{Binding ElementName=ThisWindow, Path=MyModels}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Strings" Binding="{Binding Text}" />
            <DataGridTextColumn Header="Doubles" Binding="{Binding Value}" />
        </DataGrid.Columns>
    </DataGrid>
</Window>

public partial class MainWindow : Window
{
    public MainWindow()
    {
        MyModels = new List<MyModel>
        {
            new MyModel { Text = "AAA", Value = 0.423 },
            new MyModel { Text = "BBB", Value = 0.315 },
            new MyModel { Text = "CCC", Value = 4.422 }
        };

        InitializeComponent();
    }

    public List<MyModel> MyModels { get; set; }
}
public class MyModel
{
    public string Text { get; set; }

    public double Value { get; set; }
}

If we don't specify a constructor, WPF DataGrid will default to an empty constructor, which allow users to add new row to the grid.

If I change MyModel to something like this:

public class MyModel
{
    public MyModel(string text, double value)
    {
        Text = text;
        Value = value;
    }

    public string Text { get; set; }

    public double Value { get; set; }
}

...then that extra row disappears.

If we want to make the DataGrid readonly, make sure we don't specify an empty constructor. Otherwise we need to set CanUsersAddRows to False.

Wednesday, April 1, 2015

WPF DataGrid Dynamic Numeric Formatting

I have a simple WPF DataGrid containing numbers that I want to format dynamically based on the state of 2 other WPF controls:

  • UserControl with 2 buttons: the precision
  • ComboBox: the format specifier


The User Control: Precision Adjuster

XAML:
<UserControl x:Class="DataGridFormatting.PrecisionAdjuster"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="50" d:DesignWidth="100">

    <UserControl.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Width" Value="30" />
            <Setter Property="Height" Value="30" />
            <Setter Property="Margin" Value="3" />
        </Style>   
    </UserControl.Resources>

    <WrapPanel>
        <Button Command="{Binding ReducePrecision}">
            <Image Source="Resources/DecimalLess.png" />
        </Button>
        <Button Command="{Binding IncreasePrecision}">
            <Image Source="Resources/DecimalMore.png" />
        </Button>
    </WrapPanel>
</UserControl>

Code behind:
namespace DataGridFormatting
{
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    public partial class PrecisionAdjuster : UserControl
    {
        public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register(
            "Precision",
            typeof(int),
            typeof(PrecisionAdjuster),
            new PropertyMetadata(default(int)));

        public int Precision
        {
            get { return (int)GetValue(PrecisionProperty); }
            set { SetValue(PrecisionProperty, value); }
        }

        public PrecisionAdjuster()
        {
            InitializeComponent();

            DataContext = new PrecisionAdjusterViewModel();

            Binding binding = new Binding("Precision") { Mode = BindingMode.TwoWay };
            SetBinding(PrecisionProperty, binding);
        }
    }
}

ViewModel:
namespace DataGridFormatting
{
    using System.ComponentModel;
    using System.Windows.Input;
  
    public class PrecisionAdjusterViewModel : INotifyPropertyChanged
    {
        private int precision;

        public PrecisionAdjusterViewModel()
        {
            Precision = 2;
            ReducePrecision = new RelayCommand(OnReducePrecision);
            IncreasePrecision = new RelayCommand(OnIncreasePrecision);
        }
 
        public int Precision
        {
            get { return precision; }
            set
            {
                precision = value;
                OnPropertyChanged("Precision");
            }
        }

        public ICommand ReducePrecision { get; set; } 

        public ICommand IncreasePrecision { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
 
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private void OnReducePrecision()
        {
            if (Precision > 0)
            {
                Precision--;
            }
        }

        private void OnIncreasePrecision()
        {
            Precision++;
        }
    }
}

In the code behind, we bind property Precision of the view-model with DependencyProperty Precision of the UserControl. The DependencyProperty Precision can be set outside of this UserControl, for example as the initial precision value. If this is not set, the default value is 2 as defined in the view-model. Everytime we click the button to increase or reduce precision, the precision is adjusted accordingly within the view-model. RelayCommand is a helper class implementing ICommand as described here.

The Main Window

For simplicity, I'm not going to create view-model for the MainWindow. I'll put the test data and the property we're binding to in the code behind:
namespace DataGridFormatting
{
    using System.Collections.ObjectModel;
    using System.Windows;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DataGridSource = new ObservableCollection<ItemRate>
            {
                new ItemRate("AAA", 3.0123456789),
                new ItemRate("BBB", 1.8478913937),
                new ItemRate("CCC", 2.3891383276),
                new ItemRate("DDD", 1.2334392431)
            };

            InitializeComponent();
        }

        public ObservableCollection<ItemRate> DataGridSource { get; set; }
    }
}
XAML:
(Note: when you copy and paste the below XAML to Visual Studio, it'll not recognize NumericFormatConverter. But read on, we'll create this in the next section.)
<Window x:Class="DataGridFormatting.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DataGridFormatting"
        Title="MainWindow" Height="250" Width="200"
        Name="ThisWindow">

    <Window.Resources>
        <local:NumericFormatConverter x:Key="MyNumericFormatConverter" />
    </Window.Resources>

    <StackPanel DataContext="{Binding ElementName=ThisWindow}">
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="Choose format:   "/>
            <ComboBox x:Name="FormatSpecifierInput" Width="40">
                <ComboBoxItem Content="N" IsSelected="True" />
                <ComboBoxItem Content="P" />
                <ComboBoxItem Content="C" />
            </ComboBox>
        </StackPanel>

        <local:PrecisionAdjuster x:Name="MyPrecisionAdjuster" Precision="4" HorizontalAlignment="Center" />

        <DataGrid ItemsSource="{Binding DataGridSource}"
                  AutoGenerateColumns="False" Margin="10" HorizontalAlignment="Center">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Items" Binding="{Binding Item}" />
                <DataGridTemplateColumn Header="Rates">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock>
                                <TextBlock.Text>
                                    <MultiBinding Converter="{StaticResource MyNumericFormatConverter}">
                                        <Binding Path="Rate" />
                                        <Binding ElementName="FormatSpecifierInput" Path="Text" />
                                        <Binding ElementName="MyPrecisionAdjuster" Path="Precision" />
                                    </MultiBinding>
                                </TextBlock.Text>
                            </TextBlock>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
</Window>

First we create a ComboBox containing the format we want to display our rates in. In this example, the possible values are N (Number), P (Percent) and C (Currency). I'm using .NET standard numeric format strings as described here. Name the ComboBox FormatSpecifierInput, so that we can bind this to the DataGrid later on.

Next we put the UserControl PrecisionAdjuster we created earlier into the Window. Name this control MyPrecisionAdjuster, set the initial Precision to 4.

The DataGrid has 2 columns with ItemsSource bound to property DataGridSource of type ObservableCollection<ItemRate>. The first column is DataGridTextColumn bound to property Item of class ItemRate.

    public class ItemRate
    {
        public ItemRate(string item, double rate)
        {
            Item = item;
            Rate = rate;
        }

        public string Item { get; set; }

        public double Rate { get; set; }
    }

The second column is a DataGridTemplateColumn TextBlock with Text property bound to:

  1. Property Rate of ObservableCollection<ItemRate> (bound to property DataGridSource)
  2. Property Text of the ComboBox FormatSpecifierInput
  3. DependencyProperty Precision of the UserControl MyPrecisionAdjuster
Use MultiBinding to describe a collection of Binding objects attached to a single binding target property. The Converter property is a IMultiValueConverter. We define the class that implements IMultiValueConverter in Windows.Resources. Note that at this point in time, we have yet to create this multi value converter class. Let's name this class NumericFormatConverter.


The IMultiValueConverter implementation: NumericFormatConverter

namespace DataGridFormatting
{
    using System;
    using System.Globalization;
    using System.Windows.Data;

    public class NumericFormatConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            double number = System.Convert.ToDouble(values[0]);
            string formatSpecifier = System.Convert.ToString(values[1]);
            int precision = System.Convert.ToInt16(values[2]);
            string format = "{0:" + formatSpecifier + precision + "}";  // eg. {0:P4}, {0:N2} 
            return string.Format(format, number);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Note that the order of object[] values is important. If we change the MultiBinding order in the XAML to something like this for example:

<MultiBinding Converter="{StaticResource MyNumericFormatConverter}">
    <Binding ElementName="FormatSpecifierInput" Path="Text" />
    <Binding Path="Rate" />
    <Binding ElementName="MyPrecisionAdjuster" Path="Precision" />
</MultiBinding>

Then we'll need to change NumericFormatConverter to:
    string formatSpecifier = System.Convert.ToString(values[0]);
    double number = System.Convert.ToDouble(values[1]);
    int precision = System.Convert.ToInt16(values[2]);

You might want to add some validation in the Convert method to warn your fellow developers to pay attention to the order.

Source Code

https://github.com/velianarie/DataGridFormatting