Quantcast
Channel: Developing Using CodeFluent Entities – The CodeFluent Entities Blog
Viewing all 81 articles
Browse latest View live

Persistent List

$
0
0

CodeFluent Entities has a very powerful type system. But do you know you can persist a list of string, a list of integer or another kind of list (double, boolean, etc.)?

CodeFluent Entities maps all known types to their equivalent when switching from one layer to another. On the other hand, for any other “unknown” type, CodeFluent Entities relies on several kinds of serialization: binary serialization, XML serialization, and Lightweight serialization.

So if you use a List<string> the data will be persisted by serializing it as XML or Binary depending on your configuration. If you prefer to store the list as comma separated values you can use the PersitentList from the CodeFluent Runtime.

Persistent List Choose Type Name

 

The code is very easy to use:

Customer customer = new Customer();
customer.Name = "John Doe";
customer.Contacts = 
           new CodeFluent.Runtime.Utilities.PersistentList<string>('|');
customer.Contacts.Add("Jane");
customer.Contacts.Add("Bob");
customer.Contacts.Add("Ashley");
customer.Save();

In the database the row is stored as text:

Persistent List Database

But where is the magic?

This PersistentList implements the ICodeFluentSerializable interface:

/// <summary>
/// Allows an object to control its own serialization and deserialization in CodeFluent persistence layer context.
/// </summary>
public interface ICodeFluentSerializable
{
    /// <summary>
    /// Serializes this instance.
    /// </summary>
    /// <param name="mode">The serialization mode.</param>
    /// <returns>The serialized instance. May be null.</returns>
    object Serialize(PersistenceSerializationMode mode);

    /// <summary>
    /// Deserializes the specified object instance.
    /// </summary>
    /// <param name="type">The serialized object instance type. May not be null.</param>
    /// <param name="mode">The serialization mode.</param>
    /// <param name="serializedInstance">The serialized object instance. May be null.</param>
    /// <returns>The deserialized instance. May be null.</returns>
    object Deserialize(Type type, PersistenceSerializationMode mode, object serializedInstance);
}

So there is no magic. You can implement this interface for your custom object and persist them in the database in a custom manner. I remind you that if your custom class does not implement this interface, the XML serializer or Binary serializer will be used by default.

Happy storing,

The R&D team.



SQL Server specific data types

$
0
0

CodeFluent Entities can use SQL Server specific data types such as Geography, Geometry and HierarchyId.

The first step is to register “Microsoft.SqlServer.Types.dll” into the model:

 

Add Reference

 

SQL Server Reference

Note: You must add the same reference in the BOM project.

Then set the type name of the property to Microsoft.SqlServer.Types.SqlGeography:

Type Name Geography

Choose Type SqlGeography

We also have to set the database type. As this is specific to SQL Server, we have to use the SQL Server producer attribute “sqlType”:

Sql Data Type

The same apply for Geometry and HierarchyId data types.

The table is generated, let’s add a simple method that compute intersection of two geography object. The code is really specific to SQL Server so we have to create a RAW method:

CFQL SqlGeography

Don’t forget to set the return type name of the method to SqlGeography:

Return Type Name SqlGeography

Let’s use the generated code:

Sample sample1 = new Sample();
sample1.Geography = SqlGeography.Parse("LINESTRING(-122.360 47.656, -122.343 47.656)");
sample1.Save();

Sample sample2 = new Sample();
sample2.Geography = SqlGeography.Parse("LINESTRING(-122.360 47.656, -122.343 47.656)");
sample2.Save();

var intersection = Sample.GetIntersection(sample1.Id, sample2.Id);
Console.WriteLine(intersection.ToString()); //LINESTRING (-122.34300000005148 47.656000000089243, -122.3599999999485 47.655999999910769)

If intersection is null, this means that you need to add an assembly binding in the app.config/web.config file:

<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.SqlServer.Types" culture="neutral" publicKeyToken="89845dcd8080cc91"/>        
        <bindingRedirect oldVersion="10.0.0.0" newVersion="12.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

This example shows how to use SqlGeography with CodeFluent Entities. The same works with SqlGeometry, HierarchyId, and any types that implement IBinarySerialize.

Happy storing,

The R&D Team


Using SQL Server datetime2 data type

$
0
0

CodeFluent Entities supports DateTime2 since the build 714. A datetime2 defines a date that is combined with a time of day that is based on 24-hour clock. Datetime2 can be considered as an extension of the existing datetime type that has a larger date range, a larger default fractional precision, and optional user-specified precision.

To use DateTime2 instead of DateTime you have to configure the SQL producer:

SQL Server Use datetime2

The created table uses datetime2:

CREATE TABLE [dbo].[Customer] (
 [Customer_Id] [uniqueidentifier] NOT NULL,
 [Customer_DateOfBirth] [datetime2] NULL,
)

Note: To use datetime2 in your application you have to set useDateTime2=”true” in the configuration file:

<configuration>
  <configSections>
    <section name="MyDefaultNamespace" type="CodeFluent.Runtime.CodeFluentConfigurationSectionHandler, CodeFluent.Runtime" />
  </configSections>
  <MyDefaultNamespace connectionString="..." useDateTime2=”true” />
</configuration>

DateTime2 allows to specify the precision from 0 to 7 digits. The default precision is 7. This value is configurable at property level by setting the SQL Server specify attribute “sql size”:

SQL Server Sql Size
Now the generated script looks like:

CREATE TABLE [dbo].[Customer] (
 [Customer_Id] [uniqueidentifier] NOT NULL,
 [Customer_DateOfBirth] [datetime2] (6) NULL,
)

Note that Microsoft recommends using datetime2 data type for new work:

Use the time, date, datetime2 and datetimeoffset data types for new work. These types align with the SQL Standard. They are more portable. time, datetime2 and datetimeoffset provide more seconds precision. datetimeoffsetprovides time zone support for globally deployed applications.

Happy coding,

The R&D team


“IS NOT NULL” and “IS NULL” in CFQL

$
0
0

In CodeFluent Query Language (CFQL), the SQL statement “expression IS NOT NULL” is “expression Exists” and the SQL statement “expression IS NULL” is “NOT expression EXISTS”.

There is no need to write raw SQL to test if a value exists in a method as we sometimes see in models. This can be done in full CFQL thanks to the Exists operator, this way keeping your model as platform independent as possible:

LOAD() WHERE (NOT FirstName EXISTS) AND (LastName EXISTS)

More about CFQL:

Happy CFQL-ing,

The R&D team


Persistence Tracking Columns are UTC

$
0
0

CodeFluent Entities automatically generates tracking columns. Those columns contains:

  • Creation time
  • Creation user
  • Last write time
  • Last writer user

Tracking columns

By default creation time and last write time use GETDATE function (local date of the server). We think it’s a better practice to use UTC date for this kind of data. Two years ago, we wrote an aspect to replace GETDATE function by GETUTCDATE function: http://www.softfluent.com/forums/codefluent-entities/utc-date-times.

In the latest build of CodeFluent Entities (build 772) we introduce a new built-in setting at Project Level to use UTC date instead of local date:

 

Persistence Track Columns Are UTC

This option is used by all persistence producers: Microsoft SQL Server, Microsoft SQL Azure, Oracle, MySQL and PostgreSQL.

For example with SQL Server:

CREATE TABLE [dbo].[Test] (
  [Test_Id] [uniqueidentifier] NOT NULL,
  [_trackLastWriteTime] [datetime] NOT NULL CONSTRAINT [DF_Tes__tc] 
DEFAULT (GETUTCDATE()), -- instead of getdate()
  [_trackCreationTime] [datetime] NOT NULL CONSTRAINT [DF_Tes__tk] 
DEFAULT (GETUTCDATE()), -- instead of getdate()
  [_trackLastWriteUser] [nvarchar] (64) NOT NULL,
  [_trackCreationUser] [nvarchar] (64) NOT NULL,
  [_rowVersion] [rowversion] NOT NULL
)

Happy tracking,

The R&D Team


Store Int128 in a database

$
0
0

As you may have seen in our previous posts (here and here), the type system of CodeFluent Entities is very powerful. Today we’ll see how to persist a custom type.

Let’s use the Int128 class: https://int128.codeplex.com/SourceControl/latest#SoftFluent.Int128/Int128.cs

You may notice that the class implements the IBinarySerialize interface which is natively handled by CodeFluent Entities. So by default the value will be stored in a column of type varbinary and serialized by using the IBinarySerialize interface. But the Int128 is as long as a Guid: 128bits, so it may be a better idea to store it in a column of type uniqueidentifier.

Let’s create an entity with a property of type SoftFluent.Int128, and dbType Guid:

<cf:entity name="Sample">
  <cf:property name="Id" key="true" />
  <cf:property name="GuidInt128" typeName="SoftFluent.Int128" dbType="Guid" />
</cf:entity>

Now the trick is to replace the generated code. The result code will be:

protected virtual bool BaseSave(bool force)
{
    // ...
    persistence.AddParameter("@GuidInt128", new System.Guid(this.GuidInt128.ToByteArray()), System.Guid.Empty);
    // ...
}

protected virtual void ReadRecord(System.Data.IDataReader reader, CodeFluent.Runtime.CodeFluentReloadOptions options)
{
    // ...

    this._guidInt128 = new SoftFluent.Int128(CodeFluentPersistence.GetReaderValue(reader, "GuidInt128", System.Guid.Empty));
    // ...
}

The BOM producer understands some custom attributes. Among them, three are useful in our case:

  • addParameterExpression=”<attribute value>”
  • addParameterMethodName=”<attribute value>”
  • readValueExpression=”<attribute value>”

The first one allows to define the code when adding the parameter (BaseSave method):

persistence.AddParameter("@GuidInt128", <attribute value>, System.Guid.Empty);

The second one allows to change the method AddParameter by something else:

persistence.<attribute value>("@GuidInt128", this.GuidInt128, System.Guid.Empty);

The third one allows to define the code when reading the value from the DataReader:

this._guidInt128 = <attribute value>;

One way to store value as Guid is to use the combination of addParameterExpression and readValueExpression:

<cf:property name="GuidInt128"
              typeName="SoftFluent.Int128"
              dbType="Guid"
              xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1"
              cfom:readValueExpression="new SoftFluent.Int128(CodeFluentPersistence.GetReaderValue(reader, &quot;GuidInt128&quot;, System.Guid.Empty))"
              cfom:addParameterExpression="persistence.AddParameter(&quot;@GuidInt128&quot;, new System.Guid(this.GuidInt128.ToByteArray()), System.Guid.Empty)" />

The second way to store value as Guid is to use addParameterMethodName and readValueExpression and to write an extension method:

<cf:property name="GuidInt128"
              xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1"
              cfom:readValueExpression="new SoftFluent.Int128(CodeFluentPersistence.GetReaderValue(reader, &quot;GuidInt128&quot;, System.Guid.Empty))"
              cfom:addParameterMethodName="AddParameter"
              typeName="SoftFluent.Int128"
              dbType="Guid" />

And:

public static class PersistenceUtilities
{
    public static void AddParameter(this CodeFluentPersistence persistence, string name, Int128 value, Type type, PersistenceSerializationMode mode)
    {
        persistence.AddParameter(name, new Guid(value.ToByteArray()), Guid.Empty);
    }
}

This way the AddParameter will use the extension method.

When you have more than one custom property you may want to write an aspect to automate this process.

Thanks to the power of CodeFluent Entities, we have a column of type Guid and a property of type Int128, and all of this without extra code such as adding one unneeded property to convert value from Int128 to Guid.

Happy storing,

The R&D Team


CodeFluent Runtime Database

$
0
0

CodeFluent Entities comes with lots of useful DLL. Of course you all know CodeFluent.Runtime.dll or CodeFluent.Runtime.Web.dll. Today we’ll have a look at CodeFluent.Runtime.Database.dll.

The Runtime.Database DLL contains code to explore databases (schema, tables, views, columns, primary keys, constraints, stored procedures, and data). Currently, it supports:

  • SQL Server,
  • Oracle,
  • MySQL,
  • PostgreSQL,
  • SqlLite,
  • Sql Server CE
  • Access,
  • OleDb,
  • Xmi,
  • EnterpriseArchitect

Let’s see an example:

CodeFluent.Runtime.Database.Management.Database database = new CodeFluent.Runtime.Database.Management.SqlServer.Database("Server=(local)\\SQL2014;Database=Sample;Trusted_Connection=True;");

foreach (var table in database.Tables)
{
    Console.WriteLine(table.FullName);

    foreach (var column in table.Columns)
    {
        Console.WriteLine("    {0} {1}", column.EscapedName, column.CodeFluentType.DataType);
    }

    Console.WriteLine("    PK ({0})", ConvertUtilities.ConcatenateCollection(table.PrimaryKey.Columns, "EscapedName", ", "));

    table.MaxRows = 10; // Read only 10 rows
    Console.WriteLine(DatabaseUtilities.Trace(table.Data));
}

The Database class provides a very great abstraction of the database structure. Whatever the DBMS you use, the code is the same. For example if you are using MySQL, just replace CodeFluent.Runtime.Database.Management.SqlServer.Database by CodeFluent.Runtime.MySQL.Management.Database (located in CodeFluent.Runtime.MySQL) and run the code!

The DLL also contains code to execute SQL queries or SQL scripts.

Moreover you’ll find some graphical components to create or edit connection strings (the ones used by CodeFluent Entities):

Happy exploring,

The R&D team


How to enable Intellisense for CodeFluent Entities runtime configuration?

$
0
0

CodeFluent Entities generated code can be configured using the application configuration file (App.config or web.config):

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

The configuration section is describe in the documentation: http://www.softfluent.com/documentation/BOM_ApplicationConfiguration.html.

This section is not known by Visual Studio, so you may have the following message:

ErrorXML

In fact Visual Studio doesn’t have an XML schema for this section and so it can’t validate it. At the same time we cannot provide a generic schema as the section name is Project specific. So the solution is to use The Template producer  which will generate the XML  schema based on the information from your Project.

  1.  

  2. Add a template producer

     

  3. Build the model
  4. Open the app.config or web.config file and add the following xml attributes (replace <Default namespace> by your project default namespace)

<configuration

xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;

xsi:schemaLocation=”http://www.softfluent.com/<Default Namespace>/CodeFluentConfiguration.xsd CodeFluentConfiguration.xsd”>

You should now have auto-completion of the CodeFluent Entities configuration section.

binary services

 

 

 

 

 

 

 

Note: this xml schema includes only most common settings. If you think we forgot some useful settings, please leave a comment.

Happy configuration,

The R&D Team



Export your model as image

$
0
0

Do you know that it’s possible to save the current Surface display in an image file ? It’s the easy way to share your model with someone.

You just need to right-click somewhere in the selected surface. Then you’ll see a new contextual menu with the following command:

Many formats are supported: PNG, BMP, GIF, WDP, JPG, and TIFF.

Here are some examples:

Happy exporting,

The R&D Team.


Member Format Expression

$
0
0

Using CodeFluent Entities, you can use “Member Format Expressions” to customize the way names of entities and properties are formatted. We’ve already written before about the member format expression:

Today I’ll share the property format expression I use everyday. By default entities display property name and optionally type name:

Let’s create a new property format expression. Click the “Property Format” menu Item / “Choose”. Click the “Add New” button and copy the following format expression:

<if condition=IsEntityDisplay>-> </if> <if condition=IsNullable>({Name})<else />{Name}</if> : {DisplayTypeName} <if condition=”‘true’=Element.GetAttribute(‘localizable’,’http://www.softfluent.com/codefluent/patterns/localization/2008/1&#8242;)”>(Localizable)</if> <if condition=Relations.Count>({CascadeDelete})</if>

Now entities look like:

  1. The icon indicates the property is a member of the primary key
  2. Parentheses indicate the property is nullable
  3. “(Localizable)” indicates the property is localizable when using the Localization aspect
  4. “->” indicates the property is the entity display name
  5. The icon indicates the property type is an enumeration
  6. “(None)”, “(Before)” or “(After)” indicates the selected Cascade Delete option of the relation
  7. “*” and the infinite symbol indicate the property type is a collection

Thanks to the extensibility of the CodeFluent Entities modeler you can customize your working environment to fit your need. With this property member format, you visualize lots of information about your model in a breeze. Can you do that without CodeFluent Entities?

Happy formatting !

The R&D Team


Getting started with the Blob Handler

$
0
0

In ASP.NET, if you need to display a blob (Binary Large Object) such as an image, a video or a file, you’ll need to provide an URL to your display control. When storing blobs in database, a standard practice is to create a web page such as a blob.aspx to which you provide the blob’s id as parameter and which the page will load and display so that you can set your display control’s source to that URL.

Actually, CodeFluent Entities automatically generates a HTTP Handler which does all the work described before: thanks to it you’ll retrieve an URL pointing to your blob and which you’ll be able to use in your display controls.

To use it you need to register it in your web.config file as:

<system.web>
    <httpHandlers>
      <add verb="GET" path="blobhandler.ashx" 
type="<DefaultNamespace>.Web.HttpHandler, <DefaultNamespace>"/>
    </httpHandlers>
</system.web>

Or

<system.webServer>
    <httpHandlers>
      <add name="blobhandler" verb="GET" path="blobhandler.ashx"
type="<DefaultNamespace>.Web.HttpHandler, <DefaultNamespace>" />
    </httpHandlers>
</system.webServer>

Blob handler output types

 

The blob handler allows to get blobs in different manners to fit with common usages.

  • Raw: Use for downloading a File
  • Image: Use for displaying an Image

  • Thumbnail: Display a thumbnail of the Image (you can specify the desired height and width)


  • FileExtension: Display the File icon base on its extension (the same icon as in Windows Explorer)

  • FileExtensionSmall: Display the small Fileicon base on its extension (the same icon as in Windows Explorer)

To build the URL, you can use:

  • HttpHandler.BuildUrl(…)
  • BaseBinaryLargeObject.BuildHttpHandlerUrl(…)
  • ASP.Net controls: BinaryLargeObjectControl, BlobControl and BinaryLargeObjectField (located in CodeFluent.Runtime.Web)

For instance:

customer.Photo.BuildHttpHandlerUrl(BinaryLargeObjectUrlType.Raw)
customer.Photo.BuildHttpHandlerUrl(BinaryLargeObjectUrlType.Image)

Securing the blob handler

 

If you want to prevent some users to access the blob handler and so download files, you can write your own validation logic:

  • Add a partial class “HttpHandler.partial.cs”:

  • Then override the ResponseWriteBlob method and add your logic:
partial class HttpHandler
{
protected override bool ResponseWriteBlob(CodeFluent.Runtime.BinaryServices.BinaryLargeObject blob)
  {
    // Only administrators can access the blob handler
    return this.CodeFluentContext.User.IsInRole("Administrator");
  }
}

Server and client cache

 

Moreover blobs can be cached on the server and on the client to relieve the database. This feature is activated by default.

  • Server cache: When loading a blob property using CodeFluent Entities, it actually loads it from the database the first time and stores it on the disk; consequently, next calls won’t load it back from database anymore, unless a modification was done on the blob which will reload it and update the cache.
  • Client cache: Usage of the “Last-Modified” and “If-Modified-Since” http headers so the client download blobs only once.

Read more details about blob caching at http://blog.codefluententities.com/2011/11/09/codefluent-entities-blob-cache/

Happy storing,

The R&D Team


CodeFluent Entities and ComponentOne

$
0
0

CodeFluent Entities generates code which can be used easily with many third party component providers. We already show before how to use CodeFluent Entities with Syncfusion. Today we’ll see how easy it is to work with ComponentOne (C1) WPF components!

The sample application displays a list of users and their contacts using a ComponentOne DataGrid. Additionally you can export user list to Excel.

The solution contains 4 projects:

The CodeFluent Entities model is very simple:

The email has a validation rule to ensure you can only save a user with an invalid email address to the database.

The relation between User and Contact is configured to save contacts after user. This means that when you call User.Save, associated contacts are also saved. This functionality is very useful in a master-detail view as we are creating!

Now we can create the WPF application. Here’s the main part of the XAML:

<Window.Resources>
<!-- Convert blob to image -->
<design:BinaryLargeObjectValueConverter2 x:Key="BlobConverter"/>
</Window.Resources>

<Grid>

<c1:C1DataGrid x:Name="DataGrid" ItemsSource="{Binding}" AutoGenerateColumns="False" RowDetailsVisibilityMode="VisibleWhenSelected">
  <c1:C1DataGrid.Columns>
    <c1:DataGridImageColumn Binding="{Binding Photo, Converter={StaticResource BlobConverter}}" Header="Photo" IsReadOnly="True" />
    <c1:DataGridTextColumn Binding="{Binding FirstName}" Header="First name" />
    <c1:DataGridTextColumn Binding="{Binding LastName}" Header="Last name"  />
    <c1:DataGridTextColumn Binding="{Binding Email}" Header="Email"  />
    <c1:DataGridBoundColumn Binding="{Binding Contacts.Count}" Header="Contacts" IsReadOnly="True" />
  </c1:C1DataGrid.Columns>

  <!-- Handle validation using IDataErrorInfo (this will validate the Email property) -->
  <c1:C1ValidationBehavior.ValidationBehavior>
    <c1:C1ValidationBehavior/>
  </c1:C1ValidationBehavior.ValidationBehavior>

  <c1:C1DataGrid.RowDetailsTemplate>
    <DataTemplate>
      <StackPanel Orientation="Vertical">
        <TextBlock Text="Contacts" FontSize="14"/>

        <c1:C1DataGrid ItemsSource="{Binding Contacts}" AutoGenerateColumns="False" BeginningNewRow="C1DataGrid_BeginningNewRow">
          <c1:C1DataGrid.Columns>
            <c1:DataGridTextColumn Binding="{Binding FirstName}" Header="First name" />
            <c1:DataGridTextColumn Binding="{Binding LastName}" Header="Last name" />
          </c1:C1DataGrid.Columns>
        </c1:C1DataGrid>
      </StackPanel>
    </DataTemplate>
  </c1:C1DataGrid.RowDetailsTemplate>
</c1:C1DataGrid>

<Button Grid.Row="1" HorizontalAlignment="Left" Click="ButtonExportToExcel_OnClick">Export Users to Excel</Button>
<Button Grid.Row="1" HorizontalAlignment="Right" Click="ButtonSaveAll_OnClick">Save all</Button>

</Grid>

When the window is opened, we load all users:

private readonly UserCollection _userCollection;

public MainWindow()
{
  // Load all users and bind them to the grid
  _userCollection = UserCollection.LoadAll();

  this.DataContext = _userCollection;
}

To save all users and their contacts, we have to call SaveAll method:

private void ButtonSaveAll_OnClick(object sender, RoutedEventArgs e)
{
   // Thanks to the cascade save, contacts are also saved
   _userCollection.SaveAll();
}

When a contact is added, we have to set its User property with the selected user:

private void C1DataGrid_BeginningNewRow(object sender,
DataGridBeginningNewRowEventArgs e)
{
  var contact = e.Item as Contact;

  if (contact == null)
    return;

  var user = DataGrid.CurrentRow.DataItem as User;

  if (user != null)
  {
    contact.User = user;
  }
}

Finally we can export user collection to Excel:

private
void ButtonExportToExcel_OnClick(object sender, RoutedEventArgs e)
{
  DataGrid.Save("export.xlsx", FileFormat.Xlsx);
}

That’s it. With only a few lines of code, CodeFluent Entities and ComponentOne you can create a fully functional application.

The code sample is available on our GitHub repository: https://github.com/SoftFluent/CodeFluent-Entities/tree/master/Samples/SoftFluent.Samples.ComponentOne

Happy componenting,

The R&D Team


Target Name Transformation aka TNT

$
0
0

CodeFluent Entities allows you to write RAW methods. Using the name of a table or a column in a RAW method is not safe. Indeed CodeFluent Entities allows to define its own naming convention. So if you write the name of a column in a raw method and then you change the naming convention of your project, your method won’t work anymore.

To handle this case, CodeFluent Entities introduce Target Name Transformation (aka TNT). In a Raw method you can refers to a column by using for example “$Customer::DateOfBirth$”. This will be replaced by CodeFluent Entities by the name of the column corresponding to the property “DateOfBirth” of the entity “Customer”.

TNT supports the following syntaxes:

  • $[EntityName]$ corresponds to the table name,
  • $[PropertyName]$ corresponds to the property name,
  • $[EntityName]::[PropertyName]$ corresponds to the column name,
  • $[EntityName]:[ViewName]$ corresponds to the view name,
  • $[EntityName]:[ViewName]:[PropertyName]$ corresponds to a column name in the defined view,
  • $[Namespace].[EnumerationName].[EnumerationValue]$ corresponds to the enumeration value of an enumeration declared in the model.

We already talk about all of this. So today I’ll show you more.

When you write “$[EntityName]::[PropertyName]$”, CodeFluent Entities find the property and write its persistence name. But you can also specify a format by using curly brackets. Format allows you to navigate in the CodeFluent Entities API and get the desired value. Here’s some examples:

$Customer{Columns}$

[Customer].[Customer_Id],[Customer].[Customer_Name],[Customer].[Customer_DateOfBirth],
[Customer].[_trackLastWriteTime],[Customer].[_trackCreationTime],
[Customer].[_trackLastWriteUser],[Customer].[_trackCreationUser],
[Customer].[_rowVersion] 

$Customer:CustomView{Columns}$

[vCustomerCustomView].[Customer_Id],[vCustomerCustomView].[Customer_Name],
[vCustomerCustomView].[_rowVersion],[vCustomerCustomView].[_trackCreationTime],
[vCustomerCustomView].[_trackLastWriteTime],[vCustomerCustomView].[_trackCreationUser],
[vCustomerCustomView].[_trackLastWriteUser]

$Customer{TrackCreationUserColumn.FullName}$

[Customer].[_trackCreationUser]

$Customer{TrackLastWriteUserColumn.FullName}$

[Customer].[_trackLastWriteUser]

$Customer{TrackCreationTimeColumn.FullName}$

[Customer].[_trackCreationTime]

$Customer{TrackLastWriteTimeColumn.FullName}$

[Customer].[_trackLastWriteTime]

$Customer{RowVersionColumn.FullName}$

[Customer].[_rowVersion]

$Customer{TypeNameColumn.FullName}$

[Customer].[_typeName]

$Customer{Schema}$

Sample

$Customer{Entity.Properties.Count}$

3

$Customer{Columns[0].Name}$

Customer_Id

$Customer{Entity.Properties["Name"].Column.Name}$

Customer_Name

$FirstName{Name}$

FirstName

$Customer::Name{DefaultValue}$

John Doe

$Customer::Id{FullPersistenceName}$

[Customer].[Customer_Id]

$Customer:CustomView:FirstName{Expression}$

FirstName

$Customer{Procedures["Customer_Load"].FullName}$

[Customer_Load]

To find all possible formats, open Visual Studio Object Browser (Menu / View / Object Browser), and add “C:\Program Files (x86)\SoftFluent\CodeFluent\Modeler\CodeFluent.Model.dll” and look at properties of classes:

  • CodeFluent.Model.Persistence.Table
  • CodeFluent.Model.Property
  • CodeFluent.Model.Persistence.View
  • CodeFluent.Model.ViewProperty
  • CodeFluent.Model.Enumeration
  • CodeFluent.Model.EnumerationValue

If you can’t figure how to get a specific information from your model, please ask your question on the forum.

Happy TNTing,

The R&D Team


CodeFluent Entities and SignalR

$
0
0

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


Multi-tenant using multiple Schemas

$
0
0

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



Custom Naming Convention

$
0
0

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


Table-Valued Parameters: Basics

$
0
0

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.


Table-Valued Parameters: Basics

$
0
0

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


Team Work with CodeFluent Entities

$
0
0

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 you 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.


CodeFluent Entities and Visual Studio 2015

$
0
0

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.


Viewing all 81 articles
Browse latest View live