Archive

Archive for December, 2014

What’s new with CodeFluent Entities 801?

December 19, 2014 Leave a comment

Today we release the build 801. This build includes lots of new features:

Cache Producer and array

 

The Cache Producer creates a unique key for each method call based on the method name and its parameter values:

cacheKey = SimpleCacheManager.BuildCacheKey("ad5a4755bc1df9345e1a73e2ae38",
                                             pageIndex, pageSize, 
                                             pageOptions, ids);

The BuildCacheKey method handles common parameter types such as String, Boolean, Integer, DateTime, etc. But it does not handle complex types such as array for the simple reason that there are many ways to handle them. We decided to let you handle those cases as you want depending of your context. We introduce a new optional parameter “Build Cache Key Method Name”:

The generated code will be:

cacheKey = CustomerCollection.CustomBuildCacheKey("ad5a4755bc1df9345e1a73e2ae38",
                                                   pageIndex, pageSize, 
                                                   pageOptions, ids);

This way you can create the key the way you want!

Use existing .NET enumeration and type

 

CodeFluent Entities can reuse existing .NET enumeration or .NET type. The modeler now lets you add those type on the surface:

The enumeration and all its values will be added to the surface:

The same apply with .NET type such as “System.Drawing.Point”

SQL Server Producer & “Nullable” Table Value Parameters

 

When you create a CFQL method with persistence nullable array parameter:

LOAD(guid[]
ids?) WHERE Id IN (@ids)

The SQL Server producer generates the following code:

DECLARE @_c_ids int; SELECT @_c_ids= COUNT(*) FROM @ids
IF(@_c_ids = 0)
BEGIN
    SELECT [Customer].[Customer_Id], [Customer].[Customer_FirstName], 
           [Customer].[Customer_LastName]
        FROM [Customer]
END
ELSE
BEGIN
    SELECT [Customer].[Customer_Id], [Customer].[Customer_FirstName], 
           [Customer].[Customer_LastName]
        FROM [Customer]
        WHERE [Customer].[Customer_Id] IN (SELECT * FROM @ids)
END

IS NULL is translated to Array is empty because TVP cannot be NULL. We think this check is the most logical in this case.

Starter Wizard

 

Starter wizard supports new project templates:

Pivot Script improvements

 

The SQL Server Pivot Script producer has many new options to select what to produce (tables, procedures, functions, etc.):

Sorting Project Resources

 

Project resources grid allows to sort rows by name, class or value:

Visual Studio 2015 Preview

 

As we mentioned this week, CodeFluent Entities now supports Visual Studio 2015 Preview.

codefluent2015

You can download this build 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 downloading!

The R&D Team

Categories: News

Visual Studio 2015 Preview

December 15, 2014 Leave a comment

One month ago, we told you that CodeFluent Entities runs great on Visual Studio 2013 Community. The story continues and CodeFluent Entities now supports Visual Studio 2015 Preview.

visual studio 2015

Indeed, a preview of VS 2015 is available for those ready to dive into next-generation development tool. We’re especially excited to announce that you can create or open your CodeFluent Entities models directly from Visual Studio 2015 :

codefluent2015

You can learn more about the new features of Visual Studio 2015 Preview here:

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 downloading,

The R&D Team

Categories: News Tags:

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