Archive

Archive for the ‘Developing Using CodeFluent Entities’ Category

Persistence Tracking Columns are UTC

August 7, 2014 Leave a comment

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

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

July 31, 2014 Leave a comment

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

Using SQL Server datetime2 data type

July 28, 2014 1 comment

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

SQL Server specific data types

July 24, 2014 1 comment

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

Persistent List

July 16, 2014 1 comment

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.

Store Enums as Strings


CodeFluent Entities fully suports .NET enumerations since 2005. This supports includes:

  • Being able to create your own enumerations at design time,
  • Use already existing CLR enumerations.
  • Using our designer, you can create and use enumerations in your model (e.g. OrderStatus in the screenshot below):

Enumeration

Currently CodeFluent Entities allows to store the enumeration value as a numeric value. Sometimes you prefer storing it as text so we write an aspect to do that.

Without the Aspect:
Without the aspect

With the Aspect:
With the Aspect

After adding the Aspect, you’ll see new properties in the property grid:

Store Enumeration As Text Property

The “Store Enumeration As Text” property exists at:

  • Enumeration level
  • Property level

You can also specified the column size. By default the column size will be computed from the enumeration values when possible. For example if you have a flag enumeration with values “First”, “Second”, “Third” the longest value will be “First, Second, Third”, so the column size will be 21. Of course you can override the value by setting the property “Default Column Size” at enumeration level or “Column Size” at property level.

How to install the aspect

Create a C# project in the solution and copy the Aspect files: https://github.com/SoftFluent/CodeFluent-Entities/blob/master/Extensions/SoftFluent.StoreEnumAsText/SoftFluent.Samples.StoreEnumAsText.Aspects/StoreEnumerationAsTextAspect.cs

Add a reference in the model to the aspect project:

Solution Explorer

 

Then, select those two projects:

 

Add Reference

Finally, add the Aspect into your model:

Add Existing Aspect

The code of the aspect is available on GitHub repository. Please leave feedback on how you liked this Aspect and what we could improve. You can also find additional resources about Aspects here.

Happy Aspecting!

The R&D team.

CFQL Raw methods

July 3, 2014 2 comments

CodeFluent Query Language (aka CFQL) allows developers to define platform agnostic methods. For exemple you can write:

LOAD(FirstName) WHERE FirstName STARTSWITH @FirstName 

This CFQL method will be translated to SQL by the persistence producer you decide to use. This is very powerful but when you want to write advanced procedures, you have to write platform specific code.

Read more about CFQL: http://blog.codefluententities.com/2013/07/25/hands-on-cfql/.

Inline SQL code

You can inline SQL code in CFQL by surrounding it with “[“ and “]”:

LOAD(FirstName, int year) WHERE FirstName STARTSWITH @FirstName AND [DATEPART(yy, $Customer::DateOfBirth$) = @year] 

You have to set CheckLevel to None to confirm that you understand what you are doing:

Method Properties

The SQL code generated:

CREATE PROCEDURE [dbo].[Customer_LoadByFirstNameAndYear]
(
 @FirstName [nvarchar] (256),
 @year [int],
 @_orderBy0 [nvarchar] (64) = NULL,
 @_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
SELECT DISTINCT [Customer].[Customer_Id], [Customer].[Customer_FirstName], [Customer].[Customer_LastName], [Customer].[Customer_DateOfBirth] 
    FROM [Customer]
    WHERE (([Customer].[Customer_FirstName] LIKE (@FirstName + '%')) AND DATEPART(yy, [Customer].[Customer_DateOfBirth]) = @year)

SQL method

Sometimes you need to write a custom method directly in SQL. Set the text to:

  • LOAD(arguments) RAW when the return type correspond to a collection of the current entity
  • LOADONE(arguments) RAW when the return type correspond to one instance of the current entity
  • RAW(arguments) otherwise. You have to specify the return type in the property grid

If you want to target multiple DBMS, you have to write one raw body by DBMS:

CodeFluent Query Language Editor

SQL code generated:

CREATE PROCEDURE [dbo].[Customer_LoadRaw]
(
 @year [int],
 @_orderBy0 [nvarchar] (64) = NULL,
 @_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
SELECT * FROM Customer 
WHERE DATEPART(yy, [Customer].[Customer_DateOfBirth]) = @year

Use persistent view

Persistent views will be translated to SQL views. Persistent views are a key point since they can be used:

  • To focus, simplify, and customize the perception each user has of the database.
  • To control access to rows and columns of data.
  • To aggregate data for performance.

Persistent views in CodeFluent Entities are attached to the entity concept: an entity can have multiple views. Views can be used for CFQL methods Load, LoadOne, Count, Delete and Search
Views can be auto-generated or you can provide the SQL code.

Create the view:

View Editor

CustomerView

Use the view in a CFQL method:

LoadFromView

The generated SQL code:

CREATE VIEW [dbo].[vCustomerCustomerView]
AS
SELECT 
    [Customer].[Customer_Id], 
	[Customer].[Customer_DateOfBirth], 
	[Customer].[Customer_FirstName] + ' ' + [Customer].[Customer_LastName] AS FullName
FROM Customer

CREATE PROCEDURE [dbo].[Customer_LoadFromView]
(
 @_orderBy0 [nvarchar] (64) = NULL,
 @_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
SELECT DISTINCT [vCustomerCustomerView].[Customer_Id], [vCustomerCustomerView].[Customer_DateOfBirth], [vCustomerCustomerView].[FullName] 
    FROM [vCustomerCustomerView]
    WHERE ([vCustomerCustomerView].[FullName] LIKE 'J%')

Target Name Transformation (TNT)

Using the name of 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 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.

The full documentation is available here: http://www.softfluent.com/documentation/Methods_TargetNameTransformation.html

Happy CFQLing,

The R&D team

Dissecting the ASP.NET Identity Producer – Part 3

June 24, 2014 Leave a comment

If you’ve read our two previous posts (Part 1 and Part 2), you should know how to create a CodeFluent Entities custom producer. Now you may ask yourself how to integrate it into Microsoft Visual Studio and how to debug it.

Visual Studio Integration

Fist, to declare the producer, we have to create or edit the xml file located in “%APPDATA%\CodeFluent.Modeler.Design”.

<codeFluent.Modeler> 
    <producerDescriptors> 
      <producerDescriptor name="AspNetIdentity" displayName="Asp.Net Identity" category="Security" typeName="SoftFluent.AspNetIdentity.AspNetIdentityProducer, SoftFluent.AspNetIdentity" /> 
    </producerDescriptors> 
</codeFluent.Modeler>

Then, open Visual Studio and try to add a new producer:

AspNet Identity Producer Configuration

The property grid displays properties exposed by the producer:

public class AspNetIdentityProducer : BaseProducer
{
    [DefaultValue(false)]
    [Category("Source Production")]
    [DisplayName("Must Implement IQueryableUserStore")]
    [Description("Determines if the IQueryableUserStore interface must be implemented. WARNING: this is not a real IQueryable data source. This can be used to load all users.")]
    [ModelLevel(ModelLevel.Normal)]
    public bool MustImplementQueryableUserStore
    {
        get
        {
            return XmlUtilities.GetAttribute(Element, "implementQueryableUserStore", false);
        }
        set
        {
            XmlUtilities.SetAttribute(Element, "implementQueryableUserStore", value.ToString().ToLowerInvariant());
        }
    }
}

As you can see, parameter values are stored in the XML file. Do not create automatic properties, it won’t work!

We show that we can create custom attributes, but it can be very useful to display them in the property grid:

Custom Producer Property Grid

The BaseProducer implements the IDescribable interface, so we have to override the BuildDescriptors method.

protected override void BuildDescriptors(IList<Descriptor> descriptors)
{
    if (descriptors == null)
        return;

    descriptors.Add(BuildDescriptor(
        name: "entityType",
        typeName: typeof(EntityType).AssemblyQualifiedName,
        defaultValue: "None",
        displayName: "Entity Type",
        description: "ASP.NET Identity Entity Type.",
        targets: NodeType.Entity));

    descriptors.Add(BuildDescriptor(
        name: "propertyType",
        typeName: typeof(PropertyType).AssemblyQualifiedName,
        defaultValue: "None",
        displayName: "Property Type",
        description: "ASP.NET Identity Property Type.",
        targets: NodeType.Property));

    descriptors.Add(BuildDescriptor(
        name: "methodType",
        typeName: typeof(MethodType).AssemblyQualifiedName,
        defaultValue: "None",
        displayName: "Method Type",
        description: "ASP.NET Identity Method Type.",
        targets: NodeType.Method));

    base.BuildDescriptors(descriptors);
}

Thanks to the target, descriptors are shown only when needed. This allow to not pollute the property grid with meaningless descriptors. Note that the same descriptor can have multiple targets. Combine them with OR (“|” in C#). For example :

 NodeType.Property | NodeType.Method.

Because creating identity entities is boring, we add a form to create them automatically:

AspNet Identity Form

The two issues are:

  • How to open this form?
  • How to edit the model?

To answer the first one, CodeFluent Entities uses another interface: IDesignProducer. It allows to add menu items at the producer level.

Create Identity Entities

Once again, the BaseProducer already implements this interface so we have to override two methods: EnumerateMenus and ExecuteMenu.

protected override void BuildMenus(IList<IDesignProducerMenu> menus)
{
    base.BuildMenus(menus);

    if (menus == null)
        return;

    menus.Add(new BaseDesignMenu("Create Identity Entities", true));
}

protected override bool ExecuteMenu(IServiceProvider serviceProvider, IDictionary<string, object> context, int index)
{
    Project project = context["Project"] as Project;
    if (project == null)
        return false;

    switch (index)
    {
        case 0:
            var form = new ConfigurationForm(project);
            form.ShowDialog();
            return true;
    }

    return base.ExecuteMenu(serviceProvider, context, index);
}

Now we open the form, we need to create entities. You can see that we have access to the Project object, so just use it. Here’s the code to create a new entity:

Entity entity = new Entity();
entity.Name = entityName;
entity.Namespace = @namespace;
entity.SetAttributeValue("", "entityType", Constants.NamespaceUri, entityType);
project.Entities.Add(entity);

When you edit the model, CodeFluent Entities automatically update surfaces.

How to debug your custom producer?

One way is to start Visual Studio as administrator, so you can use the post build event to copy the generated DLL to the CodeFluent Entities directory.

xcopy “$(TargetPath)” “C:\Program Files (x86)\SoftFluent\CodeFluent\Modeler” /Y

Then you can configure the debugger to start an external program:

Program: C:\Program Files (x86)\SoftFluent\CodeFluent\Modeler\CodeFluent.Build4.exe
Command line arguments:

Producer Debugger Configuration
Another solution is to add System.Diagnostics.Debugger.Launch and System.Diagnostics.Debugger.Break in your code. This can be useful in templates.

Now you can start the debug (F5) and set breakpoints into your producer.

To conclude, this producer is quite simple, but it shows:

  • How to extends the modeler,
  • How to edit the model at design time,
  • How to generate code with Templates and CodeDom.

This is a great start when you want to write a producer. If you need more information, feel free to ask your question on the forums.

The full source code is available on our GitHub repository.

Happy producing!

The R&D Team.

Dissecting the ASP.NET Identity Producer – Part 2

June 23, 2014 Leave a comment

In our last post, we talked about some generalities regarding writing a custom producer.
Today we’ll see how does the ASP.NET Identity Producer generate C# code. Let’s remind that the full source code is available on our GitHub repository.

First and before producing code, we have to find the Role and the User entities in the model.

Do you remember the NamespaceUri? It allows you to add custom attributes in the xml file which represents the model.

<cf:entity name="User" d2p1:entityType="User" xmlns:d2p1=
"http://www.softfluent.com/codefluent/producers.aspNetIdentityProducer/2014/1"> 

</cf:entity> 

We add the entityType attribute to the entity. Xml Namespace are useful to avoid naming conflict between producers.

To find the User entity we can do the following, create an enumeration named “EntityType”:

public enum EntityType 
{ 
    None, 
    User, 
    Role, 
    UserRole, 
    Claim, 
    Login 
} 

Then, read the attribute value in the following way:

foreach (var entity in project.Entities) 
{ 
    if (entity.GetAttributeValue("entityType", 
               Constants.NamespaceUri, EntityType.None) == EntityType.User) 
    { 
        return entity; 
    } 
} 

The Produce method is a blank method so you can do what you want. When the output file is simple (with no complex logic), the easiest way is to use a template. When the logic is more complex you may want to use other mechanism such as CodeDom.

In the ASP.NET Identity producer we decided to use two templates: one for UserRole and the other one for RoleStore. CodeFluent Runtime already provides everything you need to use template. We’ll use here the SimpleTemplateProducer abstract class to simplify templates mechanisms (loading, parsing, processing). This producer takes directly the template from the resources of the producer, so you’ll have only one DLL to release. Of course the template has access to the producer instance, and so to its properties and methods.

AspNet Identity Producer

Tips: Keep the maximum of the logic in the producer, your templates will be much simpler to read

Here’s an extract from the template:

public System.Threading.Tasks.Task CreateAsync([%=TemplateProducer.IdentityRole.Entity.ClrFullTypeName%] role)
{
	if(role == null)
		throw new System.ArgumentNullException("role");

    return System.Threading.Tasks.Task.FromResult(role.Save());
}

And the code to run the template

public class RoleStoreProducer : SimpleTemplateProducer
{
    public IdentityRole IdentityRole { get; set; }
    
    protected override string DefaultNamespace
    {
        get { return Producer.Project.DefaultNamespace + Producer.WebNamespaceSuffix + ".Security"; }
    }

    protected override string DefaultTypeName
    {
        get { return "RoleStore"; }
    }

    protected override Template CreateTemplate()
    {
        var template = base.CreateTemplate();

        template.AddReferenceDirective(typeof(CodeDomBaseProducer));
        template.AddReferenceDirective(typeof(UserStoreProducer));

        return template;
    }

    public override string TargetPath
    {
        get
        {
            string path = ConvertUtilities.Nullify(XmlUtilities.GetAttribute(Producer.Element, ConvertUtilities.Camel(this.TargetName) + "TargetPath", (string)null), true);
            if (path == null)
                return BaseType.GetFilePath(Producer.TargetBaseNamespace, TypeName, Namespace, Producer.FullTargetDirectory, null);

            return Producer.GetFullRelativeDirectoryPath(path);
        }
    }
}

Note: We omit some code to make this implementation simpler

The template file is found based on the DefaultTypeName, so we don’t need to specify anything else. Instantiating the RoleStoreProducer and calling its Produce method will load the template from resources, and run it.

To implement the IUser and IRole interfaces, we need to edit the code generated by the CodeDom Producer. To do so, the way to go is to write a CodeDomSubProducer. But you can also get the instance of the BOM producer and register to the CodeDomProduction event:

var producer = project.Producers.GetProducerInstance<CodeDomProducer>();
if (producer == null)
    return;
producer.CodeDomProduction += CodeDomProducer_CodeDomProduction;

We can now handle the event and add the interface implementation:

private void CodeDomProducer_CodeDomProduction(object sender, CodeDomProductionEventArgs e)
{
    if (e.EventType == CodeDomProductionEventType.EntityCommitting)
    {
        CodeCompileUnit unit = e.Argument as CodeCompileUnit;
        if (unit == null)
            return;

        foreach (CodeNamespace ns in unit.Namespaces)
        {
            foreach (CodeTypeDeclaration typeDeclaration in ns.Types)
            {
                BaseType type = UserData.GetBaseType(typeDeclaration);
                if (type.GetAttributeValue("entityType", NamespaceUri, EntityType.None) == EntityType.User)
                {
                    // Implements IUser<TKey> & IUser
                    if (_identityUser.MustImplementGenericInterface)
                    {
                        ImplementIUser(typeDeclaration, true);
                    }

                    ImplementIUser(typeDeclaration, false);
                }
            }
        }
    }
}

private void ImplementIUser(CodeTypeDeclaration typeDeclaration, bool generic)
{
    string keyTypeName = generic ? _identityUser.KeyTypeName : typeof(string).FullName;
    var iuserCodeTypeReference = new CodeTypeReference("Microsoft.AspNet.Identity.IUser");
    var iuserGenericCodeTypeReference = new CodeTypeReference("Microsoft.AspNet.Identity.IUser");
    iuserGenericCodeTypeReference.TypeArguments.Add(keyTypeName);
    if (generic)
    {
        iuserCodeTypeReference.TypeArguments.Add(keyTypeName);
    }

    typeDeclaration.BaseTypes.Add(iuserCodeTypeReference);

    CodeMemberProperty idProperty = new CodeMemberProperty();
    idProperty.PrivateImplementationType = iuserGenericCodeTypeReference;
    idProperty.Type = new CodeTypeReference(keyTypeName);
    idProperty.Name = "Id";
    idProperty.HasSet = false;
    idProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), generic ? _identityUser.KeyPropertyName : "EntityKey")));
    typeDeclaration.Members.Add(idProperty);
}

The CodeDom can be traversed to find class declarations. The CodeDom producer annotates CodeDom elements, so we can get model concept from the CodeDom element by using UserData class. In this case we know if a class corresponds to the User entity by using UserData.GetBaseEntity.

In the last part, we’ll explain how to integrate this producer in Visual Studio and how to debug it.

The R&D Team.

Dissecting the ASP.NET Identity Producer – Part 1

June 19, 2014 Leave a comment

A few weeks ago we release a new producer: ASP.NET Identity Producer. Let’s see how we wrote it.

The main idea of the article is not to explain every line of code, but to explain the main concepts of the producer.

As a reminder the full source code of the producer is available on our GitHub repository.

What is a producer?

The main goal of a producer is to translate the model into code of any kind: C#, VB.NET, SQL, plain text. But a producer can generate what it want, or maybe don’t generate anything. It can also interact with database or file system.  You can do what you want with a producer.

First you have to implement the interface IProducer.

public interface IProducer 
{ 
    event Producer.OnProductionEventHandler Production; 
    void Initialize(Project project, Producer producer); 
    void Produce(); 
    void Terminate(); 
}

The build process uses producer this way:

Build Process

In the Initialize method you should make required initializations. Often this method simply copy the Project parameter to a field, so it can be used in the Produce method. The project object contains all information about the model (Entities, Views, Tables, etc.).

In the Produce method, you have to generate the output. This can be a complex output as the BOM or a much simpler as a summary of the project or maybe no output.
The Terminate method allows to clean resources.

This is the theory. In practice producers often inherit from a base class which already implement the IProducer interface:

  • BaseProducer
  • UIProducer
  • CodeDomBaseProducer
  • TemplateProducer
  • any existing producer

The basic code of the ASP.NET Identity producer is:

public class AspNetIdentityProducer : BaseProducer
{
    protected override string NamespaceUri
    {
        get { return Constants.NamespaceUri; }
    }

    public Version TargetFrameworkVersion
    {
        get
        {
            return new Version(4, 5);
        }
    }

    public override void Initialize(Project project, Producer producer)
    {
        base.Initialize(project, producer);
    }

    public override void Produce()
    {
    }
}

public class Constants
{
    public const string NamespaceUri = "http://www.softfluent.com/codefluent/producers.aspNetIdentityProducer/2014/1";
}

We introduce two additional notions: TargetFrameworkVersion and NamespaceUri.

The first one is the Framework version needed by the generated code. The second one is the NamespaceUri used to store custom xml attributes. You should create a unique name.

How to deploy and use the producer?

A producer is composed of one DLL that contains a class which implements IProducer. To deploy the producer, you must copy the DLL to the CodeFluent Entities directory (by default: C:\Program Files (x86)\SoftFluent\CodeFluent\Modeler). To use it, you have to declare it in the model file:

  <cf:producer name="Asp.Net Identity" typeName="CodeFluent.Producers.AspNetIdentity.AspNetIdentityProducer, CodeFluent.Producers.AspNetIdentity">
    <cf:configuration targetDirectory="..\Samples" implementQueryableUserStore="true" implementQueryableRoleStore="true" cfx:targetProjectLayout="Update" />
  </cf:producer>

The next post will cover how does the producer generate the code. In the meantime you can download the full code on our GitHub repository.

Let us know what you think!

The R&D Team.

Follow

Get every new post delivered to your Inbox.

Join 49 other followers