Archive

Archive for the ‘Developing Using CodeFluent Entities’ Category

CodeFluent Entities and Visual Studio 2015

July 24, 2015 Leave a comment

Good things come in pairs: Visual Studio 2015 is now available for download and CodeFluent Entities latest build (61214.820) runs great on it!

codefluent_vs_2015

You can learn more about Visual Studio 2015 here:

Visual Studio 2015 includes Entity Framework 7. This latest version and beyond only support code-base modeling, also know as “code first”. We are pleased to announce that we continue to support the “model first” approach by providing a graphical modeler to define your business model without needing to code. You should read our whitepaper to understand the benefits of “model first” software development.

You can download the latest version of CodeFluent Entities here, or update your version using the Licensing tool. Remember that you can follow the latest new features and bug fixes of CodeFluent Entities subscribing to this RSS.

Happy Modeling,

The R&D Team.

Team Work with CodeFluent Entities


Working on your own and working along a whole developers team are obviously not the same. As a team, you will experience issues that you would never encounter working alone.

In order to help you work in a harmless way with CodeFluent Entities in that context, we wrote a new paper about every little things you can configure while you are:

  • Designing your model collaboratively
  • Generating your own database
  • Handling work station custom connection strings
  • Using source control

You can get this paper here.

Happy Configurating,

The R&D Team.

Table-Valued Parameters: Basics

March 24, 2015 Leave a comment

You can use table-valued parameters (TVP) to send multiple rows of data to a Transact-SQL statement or a routine, such as a stored procedure or function, without creating a temporary table or many parameters.

Great news, you can use TVP with CodeFluent Entities! Let’s see how you can use TVP to bulk load rows based on a list of id.

Create a new CFQL method:

Note the usage of “[]” after the type name. The generated method is:

public static CustomerCollection LoadByIds(System.Guid[] ids)

And the generated stored procedure:

CREATE TYPE [dbo].[cf_type_Customer_LoadByIds_0] AS TABLE (
 [Item] [uniqueidentifier] NULL)
GO
CREATE PROCEDURE [dbo].[Customer_LoadByIds]
(
 @ids [dbo].[cf_type_Customer_LoadByIds_0] READONLY
)
AS
SET NOCOUNT ON
DECLARE @_c_ids int; SELECT @_c_ids= COUNT(*) FROM @ids
SELECT DISTINCT [Customer].[Customer_Id], [Customer].[Customer_FirstName], [Customer].[Customer_LastName], [Customer].[Customer_DateOfBirth]
    FROM [Customer]
    WHERE [Customer].[Customer_Id] IN (((SELECT * FROM @ids)))

RETURN
GO

Table-Valued Parameters require SQL Server 2008. Don’t forget to change the target of the SQL Server producer to use at least this version.

Additionally set Legacy String Array Mode to false :

Happy Coding,

The R&D Team.

References: http://www.softfluent.com/documentation/Methods_WorkingWithArrays.html

Table-Valued Parameters: Basics

February 11, 2015 Leave a comment

First, what is Table-Valued Parameters ?

Table-valued parameters provide an easy way to marshal multiple rows of data from a client application to SQL Server without requiring multiple round trips or special server-side logic for processing the data.

You can use table-valued parameters (TVP) to send multiple rows of data to a Transact-SQL statement or a routine, such as a stored procedure or function, without creating a temporary table or many parameters.

Great news, you can use TVP with CodeFluent Entities! Let’s see how you can use TVP to bulk load rows based on a list of id.

Create a new CFQL method:

Note the usage of “[]” after the type name. The generated method is:

public static CustomerCollection LoadByIds(System.Guid[] ids)

And the generated stored procedure:

CREATE TYPE [dbo].[cf_type_Customer_LoadByIds_0] AS TABLE (
 [Item] [uniqueidentifier] NULL)
GO
CREATE PROCEDURE [dbo].[Customer_LoadByIds]
(
 @ids [dbo].[cf_type_Customer_LoadByIds_0] READONLY
)
AS
SET NOCOUNT ON
DECLARE @_c_ids int; SELECT @_c_ids= COUNT(*) FROM @ids
SELECT DISTINCT [Customer].[Customer_Id], [Customer].[Customer_FirstName], [Customer].[Customer_LastName], [Customer].[Customer_DateOfBirth]
    FROM [Customer]
    WHERE [Customer].[Customer_Id] IN (((SELECT * FROM @ids)))

RETURN
GO

Table-Valued Parameters require at least SQL Server 2008. Don’t forget to change the target of the SQL Server producer to use at least this version. Additionally set Legacy String Array Mode to false.

Additionally set Legacy String Array Mode to false.


References: http://www.softfluent.com/documentation/Methods_WorkingWithArrays.html

Happy coding,

The R&D Team.

Custom Naming Convention

January 8, 2015 Leave a comment

Since CodeFluent Entities infers a meta-model from your model, before any producer is called to generate a single line of code, a full representation of your application is in-memory. Thanks to this inference step and the resulting meta-model, developers can apply application wide changes.

One of the possible application wide changes is to change the way all database objects are named through a naming convention. By default a set of naming conventions are provided by CodeFluent Entities:

  • FormatNamingConvention
  • LowerCaseNamingConvention
  • UpperCaseNamingConvention
  • DecamelizeNamingConvention
  • DecamelizeLowerCaseNamingConvention
  • DecamelizeUpperCaseNamingConvention

And you can also implement your own naming convention to fit your needs as we’ll see in this post.

Create the custom Naming Convention

The naming convention is a class so we create a class library project and add references to

  • CodeFluent.Runtime.dll
  • CodeFluent.Model.dll
  • CodeFluent.Model.Common.dll

Those DLL are located in the installation folder of CodeFluent Entities.

Create a class that implements IProjectNamingConvention or inherits from an existing Naming Convention:

using System.Collections;
using CodeFluent.Model.Common.Naming;
using CodeFluent.Model.Naming;
using CodeFluent.Model.Persistence;

namespace SoftFluent.Samples.CustomNamingConvention
{
    public class MyNamingConvention : FormatNamingConvention
    {
        public override string GetName(INamedObject obj, IDictionary context)
        {
            var procedure = obj as Procedure;
            if (procedure != null)
            {
                if (procedure.Table != null &&
                    procedure.Table.Entity != null &&
                    procedure.Table.Entity.Store != null)
                {
                    return procedure.Table.Entity.Store.Name + "_" + base.GetName(obj, context);
                }

                if (procedure.Method != null &&
                    procedure.Method.Entity != null &&
                    procedure.Method.Entity.Store != null)
                {
                    return procedure.Method.Entity.Store.Name + "_" + base.GetName(obj, context);
                }
            }

            return base.GetName(obj, context);
        }
    }
}

This naming convention prefix parameter name with the store name

Setting the naming convention

Add a reference in the CodeFluent entities project to your class library project:

Now you can open model project properties and set the naming convention with its full type name:

 

That’s all J Now all procedures are prefixed by the store name:

Happy naming,

The R&D Team

Multi-tenant using multiple Schemas

December 4, 2014 Leave a comment

In this post we will see how to create a multi-tenant application using one schema by tenant. The idea is to create one CodeFluent Entities model and use it to generate as many database schemas as needed.

The generated database will contains one schema by tenant:

But to keep the usage as simple as possible, only one Business Object Model is generated. The schema is selected at runtime:

Note: The following procedure uses the Custom meta-compiler host feature which is available only with CodeFluent Entities Personal or Ultimate

Generate the database

 

The idea is to keep the model untouched, so we create a console application which:

  • Load the CodeFluent Entities model
  • Change entity schema in memory (the original model won’t be changed)
  • Generate code
class Program
{
    private static string _schema;
    private static string _projectPath;
    private static bool _changeTargetDirectory;

    static void Main()
    {
        _projectPath = CommandLineUtilities.GetArgument("path", (string)null) ?? CommandLineUtilities.GetArgument(0, (string)null);
        _schema = ConvertUtilities.Nullify(CommandLineUtilities.GetArgument("schema", (string)null) ?? CommandLineUtilities.GetArgument(1, (string)null), true);
        _changeTargetDirectory = CommandLineUtilities.GetArgument("changeTargetDirectory", true);

        // Load the model
        Project project = new Project();
        project.Entities.ListChanged += Entities_ListChanged; // Change schema as soon as the entity is loaded
        project.Load(_projectPath, ProjectLoadOptions.Default);

        // Update producer target directory
        if (!string.IsNullOrEmpty(_schema) && _changeTargetDirectory)
        {
            foreach (var producer in project.Producers)
            {
                var sqlServerProducer = producer.Instance as SqlServerProducer;
                if (sqlServerProducer != null)
                {
                    sqlServerProducer.Production += SqlServerProducer_Production;
                }
            }
        }

        // Generate code
        project.Produce();
    }

    private static readonly HashSet<IProducer> _producers = new HashSet<IProducer>();
    private static void SqlServerProducer_Production(object sender, ProductionEventArgs e)
    {
        SqlServerProducer sqlServerProducer = sender as SqlServerProducer;
        if (sqlServerProducer == null)
            return;

        if (_producers.Contains(sqlServerProducer))
            return;

        sqlServerProducer.EditorTargetDirectory = Path.Combine(sqlServerProducer.EditorTargetDirectory, _schema);
        _producers.Add(sqlServerProducer);
    }

    private static void Entities_ListChanged(object sender, ListChangedEventArgs e)
    {
        if (e.ListChangedType != ListChangedType.ItemAdded)
            return;

        var entityCollection = sender as EntityCollection;
        if (entityCollection == null || e.NewIndex < 0 || e.NewIndex >= entityCollection.Count)
            return;

        Entity entity = entityCollection[e.NewIndex];
        Console.WriteLine("Changing schema of entity '{0}' from '{1}' to '{2}'", entity.ClrFullTypeName, entity.Schema, _schema);
        entity.Schema = _schema;
    }
}

That’s it… We can now use this console application to generate the persistence layer:

SoftFluent.MultiTenantGenerator.exe "Sample.Model\Sample.Model.cfxproj" "SoftFluent"
SoftFluent.MultiTenantGenerator.exe "Sample.Model\Sample.Model.cfxproj" "Contoso"

You can create a script to call program this for each tenant.

Select the right tenant at runtime

 

We generate only one Business Object Model (BOM) for all tenants. This BOM access by default to the schema specified in the CodeFluent Entities model. In our case we want to change this schema at runtime depending on the context (user, Uri, etc.).

To access the database, the generated code use CodeFluentPersistence:

CodeFluentPersistence has a hook system (ICodeFluentPersistenceHook) that allows to change the default CodeFluentPersistence behavior. In our case the idea is to change the CreateStoredProcedureCommand method behavior to use the right schema. Here’s the code:

public class SchemaPersistenceHook : BasePersistenceHook
{
    private bool _processing = false;
    public override bool BeforeCreateStoredProcedureCommand(string schema, string package, string intraPackageName, string name)
    {
        if (_processing)
            return false;

        _processing = true;
        try
        {
            string currentSchema = GetTenant();
            Persistence.CreateStoredProcedureCommand(currentSchema, package, intraPackageName, name);
        }
        finally
        {
            _processing = false;
        }

        return true;
    }

    public virtual string GetTenant()
    {
            // TODO: Implement your own logic
            return CodeFluentUser.Current.UserDomainName;
    }
}

Finally we have to declare our persistence hook in the configuration file so CodeFluentPersistence will use it automatically:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="Sample" type="CodeFluent.Runtime.CodeFluentConfigurationSectionHandler, CodeFluent.Runtime" />
  </configSections>

  <Sample persistenceHookTypeName="Sample.SchemaPersistenceHook, Sample" />
</configuration>

That’s it. With a few lines of codes and the power of CodeFluent Entities you can change the default behavior to fit your needs. Can you do the same with other products?

The source code is available on our GitHub repository.

Happy tenanting,

The R&D team

CodeFluent Entities and SignalR

November 21, 2014 Leave a comment

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

Follow

Get every new post delivered to your Inbox.

Join 56 other followers