Sunday, May 15, 2011

Expressionistic copying

Recently I had the need of cloning a number of object, traditionally you’d have to use reflection based techniques if you wanted a generic solution, (unless you go really hardcore with Reflection.Emit)

The sample code below only accounts for public properties, but in many cases that isn’t enough.

Given the performance implications of using reflection, and the fact that you are allowed to include blocks in expressions in .NET 4 I figured I’d do a small experiment and micro-benchmark comparing these two approaches:

I have an entity I want to copy like so

public class Entity
{
public int Foo { get; set; }
public string Bar { get; set; }
public string Baz { get; set; }
public int Foo2 { get; set; }
public string Bar2 { get; set; }
public string Baz2 { get; set; }
public int Foo3 { get; set; }
public string Bar3 { get; set; }
public string Baz3 { get; set; }
}

I implemented reflection based copy like so (with some room for optimizations):



public class ReflectionObjectCopier : IObjectCopier
{
#region IObjectCopier Members

public void Copy<T>(T fromObj, T toObj)
{
var properties = from prop in fromObj.GetType().GetProperties(BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy)
where prop.CanRead && prop.CanWrite
select prop;
foreach (PropertyInfo pi in properties)
{
pi.SetValue(toObj, pi.GetValue(fromObj, null), null);
}
}

#endregion
}


When using this copying 10 000 objects takes 250ms or so.



However, if I create an expression that performs the copying directly it only takes 10ms (with subsequent testruns only taking 4, when reusing the cached expression). That is a pretty decent saving if you ask me.



public class ExpressionObjectCopier : IObjectCopier
{
Dictionary<Type, object> cache = new Dictionary<Type, object>();

Action<T,T> CreateCopier<T>()
{
ParameterExpression fromParam = Expression.Parameter(typeof(T), "from");
ParameterExpression toParam = Expression.Parameter(typeof(T), "to");

LambdaExpression exp = Expression.Lambda(typeof(Action<T, T>),
Expression.Block(
from prop in typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy)
where prop.CanRead && prop.CanWrite
select Expression.Assign(Expression.Property(toParam, prop),
Expression.Property(fromParam, prop))
),
fromParam,
toParam);

Action<T, T> copier = (Action<T, T>)exp.Compile();
cache[typeof(T)] = copier;
return copier;
}

#region IObjectCopier Members

public void Copy<T>(T fromObj, T toObj)
{
object action;
Action<T,T> copier;
if (!cache.TryGetValue(fromObj.GetType(), out action))
{
copier = CreateCopier<T>();
}
else
{
copier = (Action<T, T>)action;
}


copier(fromObj, toObj);
}

#endregion
}


The above code obviously isn’t production quality…

Tuesday, November 02, 2010

Timeline – or restyling controls

One nice thing with WPF that is slightly different is that the controls are lookless. This means that you can re-style or apply a different template altogether if the behavior of a standard control is what you want, but the looks are not.

A real world example: At work we recently needed to show a list of items with associated timestamps, in a way so that the time differences between them are easily determined at a glance.

Thanks to the lookless controls of WPF I was able to solve this simply by using a ListBox with re-templated ListBoxItems giving the following result:

image

The style is defined as follows:

        <Style TargetType="ListBoxItem" x:Key="TimelineStyle">
            <
Setter Property="SnapsToDevicePixels" Value="true"/>
            <
Setter Property="OverridesDefaultStyle" Value="true"/>
            <
Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <
Setter Property="Template">
                <
Setter.Value>
                    <
ControlTemplate TargetType="ListBoxItem">
                        <
StackPanel Orientation="Vertical"> <Border
                           
Name="Border"
                           
Padding="2"
                           
CornerRadius="2"
                           
BorderThickness="2"
                           
SnapsToDevicePixels="true">
                                <
ContentPresenter />
                            </
Border>
                            <
Grid>
                                <
Rectangle Width="2" Stroke="LightBlue"
                                          
Height="{Binding TimeToNext, Converter={StaticResource HeightConverter}}"
                                          
VerticalAlignment="Center"
                                          
HorizontalAlignment="Center"/>
                                <
TextBlock Text="{Binding TimeToNext, Converter={StaticResource DurationConverter}}"
                                          
Background="White"
                                          
VerticalAlignment="Center"
                                          
HorizontalAlignment="Center"
                                          
FontSize="8"
                                          
Foreground="Gray"/>
                            </
Grid>
                        </
StackPanel>
                        <
ControlTemplate.Triggers>
                            <
Trigger Property="IsSelected"
                                    
Value="true">
                                <
Setter TargetName="Border"
                                       
Property="BorderBrush"
                                       
Value="Blue"/>
                            </
Trigger>
                        </
ControlTemplate.Triggers>
                    </
ControlTemplate>
                </
Setter.Value>
            </
Setter>
        </
Style>

and and the listbox uses the style like this:

<ListBox ItemsSource="{Binding DataItems}" ItemContainerStyle="{StaticResource TimelineStyle}" />



The style uses two converters, but those should be fairly trivial.



Setting FocusVisualStyle to {x:Null} is done to hide the indication of keyboard focus, as unless it is fixed properly, will be more confusing than helpful. Fixing it properly is left as an exercise for the reader.

Saturday, February 06, 2010

MCTS

Today I passed the second certification exam 70-502. So now I’m officially a Microsoft Certified Technology Specialist: .NET Framework 3.5, Windows Presentation Foundation Applications.

Monday, February 01, 2010

Nullable<T> and IFormattable

If you ever tried to format a nullable type you would soon realize that you cannot directly as Nullable<T> does not implement IFormattable and thus you only have object.ToString() available.

This is easily fixed using an extension method:

public static class NullableExtensions
{
public static string ToString<T>(
this Nullable<T> nullable,
string format,
IFormatProvider formatProvider)
where T : struct, IFormattable
{
if (!nullable.HasValue) return string.Empty;
T notNull = nullable.Value;
return notNull.ToString(format, formatProvider);
}
}


and you can use it e.g. like so:



            DateTime? foo = null;
...
foo.ToString("t", CultureInfo.CurrentCulture);

Thursday, January 21, 2010

WPF project building inside of Visual Studio but not with MSBuild/TFSBuild

At work, I recently run into a strange build error when building from MSBuild and TFSBuild while the same solution built inside of Visual Studio 2008 just fine.

The error message was:

error MC3015: The attached property '?' is not defined on '?' or one of its base classes.

(obviously with real names instead of the questionmarks)

Feeding the above into google provided the following solution to the issue:

One of the differences between building in VS and command line is that a WPF build in VS defaults to

<AlwaysCompileMarkupFilesInSeparateDomain>true</AlwaysCompileMarkupFilesInSeparateDomain>.
Outside of VS, the default is false.

(Answer by Rob Relyea)

Adding that to the relevant .csproj made the project build successfully.

Monday, November 30, 2009

MeasureUp FAIL

I’m currently studying for the 70-536 on my way towards MCTS certification.

I have the self-paced training kit and thought I’d do the Training Kit Exam Prep “Powered by MeasureUp”

Things go without technical issues until I get to scoring.

Then I am greeted with:

---------------------------
Application Message
---------------------------
An unanticipated error has occurred in the application.InsertScoreHistory

[Microsoft OLE DB Provider for ODBC Drivers] [3604] [Microsoft][ODBC Microsoft Access Driver] Syntax error in date in query expression '#30.11.2009 21:51:40#'.
---------------------------
OK   Cancel  
---------------------------

Trying to review a particular answer fails with:

---------------------------
Application Message
---------------------------
An unanticipated error has occurred in the application.GoToQuestionSelected

[Microsoft.VisualBasic] [13] Conversion from string "7." to type 'Short' is not valid.
---------------------------
OK   Cancel  
---------------------------

I then eventually end up at the FAQ page at MeasureUp and find

I'm receiving Error: Mismatch 13.
In order for tests to display correctly, your computer's regional settings should be set to English (United States).

In the control panel, please double click on the icon labeled Regional Settings. Please choose English (United States), and click the box that says "use default properties for this input locale."

Alternatively, in the regional settings, look at the Currency tab. If it is set the decimal symbol to . (dot) instead of , (coma), that may be enough to get it working properly.

If the trouble persists, try setting the keyboard language layout to English (United States). In the control panel, double click on the icon labeled "Keyboard". Then click on the tab labeled input locales. Select the Add button. And then choose English (United States) as the input locale.

W T F !?!

It’s kinda ironic that one of the skills measured is “Implementing globalization, … in a .NET Framework application”

What’s even more tragic is that if I actually DO change to en-us, trying to start a new test fails, this time with:

---------------------------
Application Message
---------------------------
An unanticipated error has occurred in the application.PreviousSessionTestRecord

[Microsoft.VisualBasic] [13] Conversion from string "30.11.2009 21:58:26" to type 'Date' is not valid.
---------------------------
OK   Cancel  
---------------------------

Tuesday, November 10, 2009

roleProvider app.config parsing troubles

I recently had the pleasure of trying to get our RC out the door, while doing that we realized that for whatever reason, Microsoft seems to use a non-standard parser for the app.config file.

Basically this does not work:

<system.web>
  <
roleManager defaultProvider="MyCustomProvider" enabled="true">
    <
providers>
      <
clear></clear>
      <
add name="MyCustomProvider" type="Namespace.MyCustomProvider, MyAssembly"/>
    </
providers>
  </
roleManager>
</
system.web>

While this does:

<system.web>
  <
roleManager defaultProvider="MyCustomProvider" enabled="true">
    <
providers>
      <
clear/>
      <
add name="MyCustomProvider" type="Namespace.MyCustomProvider, MyAssembly"/>
    </
providers>
  </
roleManager>
</
system.web>

Of course, the well known commercial installer making tool we are using insists of creating the former… :(

A workaround is to throw the file through an identity XSLT transform in a custom tool / custom action run after custom actions from said vendor have done their job.

Oh, the bug reported on connect is already closed as Won’t Fix