Tuesday, 24 June 2025

Custom code conventions in Entity Framework

This article will once more look at code conventions in Entity Framework. A custom code convention will be added where if the property (column of an entity) is called "Key" and is of type Guid, it is set as the key of the entity (surrounding type the property resides in). The code in this article is available in my Github repo here:

https://github.com/toreaurstadboss/BulkOperationsEntityFramework

The custom code convention for setting all properties called Key of property type Guid as the key of an entity (table) looks like the following:

GuidKeyConvention.cs



using System;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Reflection;

namespace BulkOperationsEntityFramework.Conventions
{

    public class GuidKeyConvention : Convention
    {
        public GuidKeyConvention()
        {
            Types().Configure(t =>
            {
                var keyProperty = t.ClrType
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .FirstOrDefault(p => p.PropertyType == typeof(Guid)
                    && string.Equals(p.Name, "Key", StringComparison.OrdinalIgnoreCase));

                if (keyProperty != null)
                {
                    t.HasKey(keyProperty);
                }
            });            
        }
    }

}



The custom code convention can then be added in the OnModelCreating method shown below :

ApplicationDbContext.cs



 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
     modelBuilder.Entity<User>().HasKey(u => u.Id);
     modelBuilder.Entity<User>().Property(u => u.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

     modelBuilder.Properties<string>().Configure(p => p.HasMaxLength(255)); // Set max length for all string properties

     modelBuilder.Conventions.Add(new GuidKeyConvention());
     
     //more code

 }


As shown above, a custom convention inherits from the System.Data.Entity.ModelConfiguration.Conventions.Convention class. The behavior of the custom code convention is set up in the constructor of the custom code convention. It is not overriding any methods, instead it is making use over inherited public methods Types() or Properties() or Properties(). An example of an entity that then will use this custom code convention is shown with the following entity (POCO) :

Session.cs



using System;

namespace BulkOperationsEntityFramework.Models
{
    public class Session
    {

        public Guid Key { get; set; } // Primary key by convention

        public DateTime CreatedAt { get; set; }

        public DateTime? ExpiresAt { get; set; }

        public string IpAddress { get; set; }

        public string UserAgent { get; set; }
    }
}


Adding this custom code convention, the following database migration is then added and shows that the property (field/column) called Key of type Guid.


namespace BulkOperationsEntityFramework.Migrations
{
    using System.Data.Entity.Migrations;

    public partial class Sessions : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.Sessions",
                c => new
                {
                    Key = c.Guid(nullable: false),
                    CreatedAt = c.DateTime(nullable: false),
                    ExpiresAt = c.DateTime(),
                    IpAddress = c.String(maxLength: 255),
                    UserAgent = c.String(maxLength: 255),
                })
                .PrimaryKey(t => t.Key);

        }

        public override void Down()
        {
            DropTable("dbo.Sessions");
        }
    }
}


As shown in the database migration, the primary key is set to the field Key, which means that the field (property/column) Key is set as the primary key. It is of course easier to just attribute the property Key with the Key attribute or set up the primary key in the OnModelConfiguring (Fluent API). Custom code conventions are best in use when you make a custom code convention that saves a lot of setup where you have a large data model and want to standardize code conventions, this sample just is a demonstration of how such a custom code convention can be created by inheriting the Convention class in the namespace System.Data.Entity.ModelConfiguration.Conventions. In the previous article, a custom code convention encapsulated in a custom marker attribute and wiring up the logic via helper extension methods. Instead of inheriting from the Convention class, the wiring up of Schema attribute shown in previous article could be more standardized in this way. Such a SchemaConvention could be set up like this instead, inheriting Convention class.

SchemaConvention.cs




using BulkOperationsEntityFramework.Attributes;
using System.Data.Entity.Infrastructure.Pluralization;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Reflection;

namespace BulkOperationsEntityFramework.Conventions
{
    public class SchemaConvention : Convention
    {
        public SchemaConvention()
        {
            var pluralizer = new EnglishPluralizationService();

            Types().Configure(c =>
            {
                var schemaAttr = c.ClrType.GetCustomAttribute<SchemaAttribute>(false);
                var tableName = pluralizer.Pluralize(c.ClrType.Name);

                if (schemaAttr != null && !string.IsNullOrEmpty(schemaAttr.SchemaName))
                {
                    c.ToTable(tableName, schemaAttr.SchemaName ?? "dbo");
                }
                else
                {
                    c.ToTable(tableName);
                }
            });
        }
    }
}  




Share this article on LinkedIn.

No comments:

Post a Comment