Home > Developing Using CodeFluent Entities > CodeFluent Entities and SignalR

CodeFluent Entities and SignalR


ASP.NET SignalR is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is “real-time web” functionality? It’s the ability to have your server-side code push content to the connected clients as it happens, in real-time.

Let’s see how easy it is to use CodeFluent Entities with SignalR! This post introduces SignalR development by using CodeFluent Entities and showing how to create an application that shares the state of an CodeFluent entity (Customer) with other clients in real time.

Setting up the solution

The solution contains 4 projects:

  • The CodeFluent Entities model
  • A class project to contains the generated Business Object Model
  • The SignalR server (Console application)
  • The SignalR client (WPF application)

The CodeFluent Entities model
The model is very simple, just one entity:

To generate the server code we add the SQL Server Producer and the Business Object Model producer.

SignalR uses Json.NET to serialize object. The way this library finds a way to serialize an object is weird and does not works with generated object by default because of the following attribute:

[TypeConverterAttribute(typeof(CodeFluent.Runtime.Design.NameTypeConverter))]

So we have to remove it so the object is serialize correctly:

To generate the client object we add the Service Model sub producer (with the same setting as above):

Don’t forget to remove runtime design attributes:

Finally the model project looks like:

The SignalR server

The server is a Console application. First we add the “Microsoft.AspNet.SignalR.SelfHost” nuget package.

We can register the SignalR server:

class Program
{
    static void Main()
    {
        using (WebApp.Start<Startup>("http://localhost:12345"))
        {
            Console.WriteLine("Server started");
            Console.ReadKey();
        }
    }
}

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HubConfiguration hubConfiguration = new HubConfiguration();
        hubConfiguration.EnableDetailedErrors = true;
        app.MapSignalR(hubConfiguration);
    }
}

Now we can create the Customer hub:

public class CustomerHub : Hub
{
    public IEnumerable<Customer> Get()
    {
        return CustomerCollection.LoadAll();
    }

    public bool Save(Customer customer)
    {
        bool save = Customer.Save(customer);
        if (save)
            Clients.All.Saved(customer); // Notify clients

        return save;
    }

    public bool Delete(Customer customer)
    {
        bool delete = Customer.Delete(customer);
        if (delete)
            Clients.All.Deleted(customer.Id); // Notify clients

        return delete;
    }
}

The generated Business Object Model is easy to use with any technology J.

The SignalR Client

The client is a WPF application. First we need to add the “Microsoft.AspNet.SignalR.Client” nuget package.

The project already contains generated class from the model so we don’t need to create a Customer class:

Let’s create the XAML:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <Button Content="Load customers" Click="ButtonLoadCustomers_OnClick" Margin="5"/>
        </StackPanel>

        <DataGrid Grid.Row="1" x:Name="DataGrid" AutoGenerateColumns="False" RowEditEnding="DataGrid_RowEditEnding">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding EntityKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Entity Key"/>
                <DataGridTextColumn Binding="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="First Name"/>
                <DataGridTextColumn Binding="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Last Name"/>

                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button Command="Delete" Content="X" Click="ButtonDelete_OnClick" DataContext="{Binding}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

Create the connection to the server and register callbacks:

private async Task<bool> EnsureProxy()
{
    if (HubProxy != null)
        return true;

    Connection = new HubConnection(ServerUri);
    HubProxy = Connection.CreateHubProxy("CustomerHub");

    // Register callbacks
    HubProxy.On<Customer>("Saved", OnCustomerSaved);
    HubProxy.On<Guid>("Deleted", OnCustomerDeleted);
    try
    {
        await Connection.Start();
        return true;
    }
    catch (HttpRequestException)
    {
        Connection.Dispose();
        Connection = null;
        MessageBox.Show("Unable to connect to server: Start server before connecting clients.");
        return false;
    }
}

Handle events:

private void OnCustomerDeleted(Guid id)
{
    var customerCollection = DataGrid.ItemsSource as CustomerCollection;
    if (customerCollection != null)
    {
        customerCollection.Remove(id);
    }
}

private void OnCustomerSaved(Customer customer)
{
    var customerCollection = DataGrid.ItemsSource as CustomerCollection;
    if (customerCollection != null)
    {
        var c = customerCollection[customer.Id];
        if (c != null)
        {
            customer.CopyTo(c, true); // Update existing customer
        }
        else
        {
            customerCollection.Add(customer); // Add new customer
        }
    }
}

Handle UI events (load, edit, delete):

private async void ButtonLoadCustomers_OnClick(object sender, RoutedEventArgs e)
{
    if (!await EnsureProxy())
        return;

    var customers = await HubProxy.Invoke<CustomerCollection>("Get");
    if (customers == null)
        customers = new CustomerCollection();

    BindingOperations.EnableCollectionSynchronization(customers, _lock);
    DataGrid.ItemsSource = customers;
}

private async void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
    if (e.Cancel)
        return;

    var result = await HubProxy.Invoke<bool>("Save", e.Row.Item);
}

private async void ButtonDelete_OnClick(object sender, RoutedEventArgs e)
{
    var customer = ((Button)sender).DataContext as Customer;
    if (customer == null)
        return;

    if (!await EnsureProxy())
        return;

    var result = await HubProxy.Invoke<bool>("Delete", customer);
}

The Business Object Model and the Service Object Model are very easy to use with any .NET technologies such as SignalR or Web API.

If your SignalR API is as simple as the one we create, you can automate its creation with templates.

The code sample is available on our GitHub repository.

Happy Coding,

The R&D Team

  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s