Sunday, July 26, 2015

FluentData load references

Introduction

FluentData is an opensource library ( https://fluentdata.codeplex.com/ ) that enables you to access databases using direct query without to have a mapped entity framework, in fact it can work using the C# 4.0 dynamics.

I developed a simple extension method to allow you easily add the navigating properties to gathered data ( https://github.com/devel0/Lib0/tree/master/Lib0.Data.FluentData ).

Test database


The simple database that I used has two tables and a foreign key that let you navigate from the "user_entries" table to the "contact_detail" table through the "id_contact_detail" foreign key.

Test app

The test application is composed by a datagrid that lists the user_entries ids and a form detail which shows the firstname and lastname fields of the related record.






XAML part




<!--#region Master/Detail -->
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            
            <!--#region Master -->
            <DataGrid x:Name="MasterDataGrid" IsReadOnly="True">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="ID" Binding="{Binding id}"/>
                </DataGrid.Columns>
            </DataGrid>
            <!--#endregion-->

            <!--#region Detail -->
            <Grid Grid.Column="1" x:Name="DetailDataGrid" 
                  DataContextChanged="DetailDataGrid_DataContextChanged"
                  DataContext="{Binding ElementName=MasterDataGrid, Path=SelectedItem}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>                    
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                
                <TextBlock Grid.Row="0" Grid.Column="0" Text="Firstname:" VerticalAlignment="Center"/>
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding contact_detail.firstname, Mode=TwoWay}"/>
                
                <TextBlock Grid.Row="1" Grid.Column="0" Text="Lastname:" VerticalAlignment="Center"/>
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding contact_detail.lastname, Mode=TwoWay}"/>
            </Grid>
            <!--#endregion-->
        </Grid>
        <!--#endregion-->

Code part

ctx = new DbContext().ConnectionString(ConnectionStringTbox.Text, new PostgreSqlProvider());

var q = ctx.Sql("select * from user_entries").QueryMany<dynamic>();

q.LoadReferences(ctx,                
    "contact_detail",
    k => k.id_contact_detail,                
    k => k.id
    );           

MasterDataGrid.ItemsSource = q.ToList();

The extension method "LoadReferences" extends given dynamic objects that comes from the QueryMany with the property "contact_detail" that is the object gathered from the database using the foreign key.

Considerations

The extension library discussed is mostly an initial act for a deeper investigation about what can be done to make the use of the FluentData library more useful compared to the Entity Framework.

Roadmap :
  • better management of loading references
    • use of custom where to restrict the result set
    • use of join instead of load all records
    • lazy load of records
  • management of changeset
    • listen to dynamics using PropertyChanged

Creative Commons License
FluentData lazy loader by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.

Sunday, July 19, 2015

C# complete operation event handler

Introduction



Fig.1 - Event handling (normal behavior)
Usually when we want to listen an event we have to connect an event handler and then we'll receive calls to the event handler each time the event is raised from the originating source (Fig.1).

In some other scenarios we need to be informed if an event occurred event if its already sent (Fig.2).


Fig. 2 - Listen to past events

Follow is an example :

using System;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Test x = new Test();           

            x.Fire();

            x.EvTest += (a, b) =>
            {
                Console.WriteLine("Event fired");
            };
        }
    }

    public class Test
    {

        public event EventHandler EvTest;

        public void Fire()
        {
            if (EvTest != null) EvTest(this, null);
        }

    }
}

Running the above code you'll notice that the event is not intercepted because the handler is connected only after the event occurred.

Lib0.Core.EventOperation

To get rid of this problem I built a class to manage such events, called EventOperation.

Source code

Source code for this library is located at follow url https://github.com/devel0/Lib0

Example

/// <summary>
/// Be notified after the event fired.
/// </summary>
[TestMethod]
public void Test4()
{
  IEventOperation op = new EventOperation(EventOperationBehaviorTypes.RemindPastEvents);

  op.Fire();

  Assert.IsTrue(op.FireCount == 1);
  Assert.IsTrue(op.HandledCount == 0);

  op.Event += (a, b) => { }; // This handler function is called right here
                             // cause previous event already fired.
  Assert.IsTrue(op.FireCount == 1);
  Assert.IsTrue(op.HandledCount == 1);
}

EventOperation takes an optional argument to set which behavior between "Normal" or "RemindPastEvent" types.

Multiple listeners

Follow unit test demonstrate how the event operation works using "RemindPastEvent" behavior and multiple listeners :

/// <summary>
/// Be notified after the event fired (multiple listeners).
/// </summary>
[TestMethod]
public void Test5()
{
    IEventOperation op = new EventOperation(EventOperationBehaviorTypes.RemindPastEvents);

    var listener1HitCount = 0;
    var listener2HitCount = 0;

    {
        op.Fire(); // event fired ( no handlers yet connected )
        Assert.IsTrue(op.FireCount == 1 && op.HandledCount == 0);

        op.Event += (a, b) => // listener1 connects and receive 1 event
        {
            ++listener1HitCount;
        };
        Assert.IsTrue(op.FireCount == 1 && op.HandledCount == 1);
    }

    {
        op.Fire(); // event fired ( listener1 will receive its 2-th event )
        Assert.IsTrue(op.FireCount == 2 && op.HandledCount == 2);

        op.Event += (a, b) => // listener2 connected and receive 2 events
        {
            ++listener2HitCount;
        };                
    }

    Assert.IsTrue(listener1HitCount == 2 && listener2HitCount == 2);
    Assert.IsTrue(op.FireCount == 2 && op.HandledCount == listener1HitCount + listener2HitCount);            
}

A final word

RemindPastEvents behavior implies that a list of past event arguments are stored into the memory, so be aware not to use it for massive event handling. The scenario in which this class make sense can be for example the one represented in Fig2 when you need to synchronize many listeners to an event that we don't know if its already fired.


Creative Commons License
C# complete operation event handler by Lorenzo Delana is licensed under a Creative Commons Attribution 4.0 International License.