Code-Only best practices


There have been lots of posts on the EFDesign blog talking about how the features in Code Only have evolved.

But very little covering what we think will be the best way to write the code.

You may have seen code like this:

var prodConf = builder.Entity<Product>();
productConf.HasKey(p => p.Id);
productConf.Property(p => p.Id).IsIdentity();
productConf.Property(p => p.Name)
           .HasMaxLength(100);
productConf.Relationship(p => p.Category)
           .FromProperty(c => c.Products)
           .HasConstraint((p,c) => p.CategoryId == c.Id);
productConf.MapSingleType(p =>
            new {
                Id = p.Id, 
                Nme = p.Name, 
                cid = p.CategoryId
            })
            .ToTable(“dbo.Products”);

And thought that this is what we recommend you write.

Well actually it isn’t.

I’m not making excuses here, but the problem is that sometimes showing best practices gets in the way of explaining features, its so much easier to just show the feature in isolation. 

And there is nothing wrong with that… so long as you go back later and give guidance.

So here goes:

The recommended way to do this is to create classes that derive from EntityConfiguration<TEntity> and put the configuration logic in the constructor like this:

public class ProductConfiguration: EntityConfiguration<Product>
{
    public ProductConfiguration()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).IsIdentity();
        Property(p => p.Name).HasMaxLength(100);
        Relationship(p => p.Category)
            .FromProperty(c => c.Products)
            .HasConstraint((p,c) => p.CategoryId == c.Id);
        MapSingleType(p =>
             new {
                  Id = p.Id, 
                  Nme = p.Name, 
                  cid = p.CategoryId
             })
             .ToTable(“dbo.Products”);
    }
}

Next when you want to use this configuration you simply register it:

builder.Configurations.Add(new ProductConfiguration());
// & repeat for all types you want to configure

This nicely encapsulates the configuration and simplifies the code too.

Comments (3)

  1. Tony B says:

    Do you have any tips on how to use EntityConfiguration<TEntity> with TPH?

    I would like a EntityConfiguration class per Entity object but using TPH you have to do something like this:

    MapHierarchy().Case<TEntity>(λ).Case<TDerived>(λ)

    Which means one EntityConfiguration class is going to have to know about more than one Entity class.

  2. Alex D James says:

    Well you can do TPH without using Case, in which case you can have an EntityConfiguration for each type in a TPH inheritance hierarchy.

    Unfortunately this means you end up repeating the column mappings.

    So for TPH hierarcies you can have either:

    1) Separation of concerns and Redundancy

    2) Grouped concerns and no redundancy

    You can’t have both with the current design.

    Thanks for bring this up, I’ve forwarded this issue on to the rest of the Team.

    Alex

  3. Tony B says:

    Thanks for the tip.  I assumed that I had to use Case for TPH.

    Is there a way using code only to map one column (using TPH) to two different properties of different subclasses?  I can do that using EDMX but can’t reproduce using Code-Only without getting this error:

    Problem in mapping fragments starting at lines 31, 40:Column(s) [ColumnX] are being mapped in both fragments to different conceptual side properties.