Search code examples
c#entity-framework-6ef-model-builder

EF6 Set MaxLength of a property based on attribute of the containing class


Situation:

I have a base class Lookup, as follows:

 public abstract class Lookup : DeactivatableDomainModel {
    [Required]
    [Key]
    public int ID { get; set; }

    [Required]
    public string Description { get; set; }

    [Required]
    public int DisplayOrder { get; set; }
  }

I've also created an attribute IsLookup:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class IsLookup : Attribute {
  public int DescriptionLength { get; set; }
  public int CodeLength { get; set; }

  public IsLookup(int DescriptionLength, int CodeLength = 0) {
    this.DescriptionLength = DescriptionLength;
    this.CodeLength = CodeLength;
  }
}

The goal is to be able to create the following declaration:

[IsLookup(40)]
public class TestCategory : Lookup { }

...and use OnModelCreating to set the MaxLength of property Description to 40.

I've been able to code something that looks like it should work, and the add-migration runs just fine, but the resulting migration doesn't have the maxlength set:

  protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    base.OnModelCreating(modelBuilder);

    modelBuilder.Properties()
      .Where(p => p.Name == "Description" && p.DeclaringType.GetCustomAttributes(false).OfType<IsLookup>().FirstOrDefault() != null)
      .Configure(
        c => c.HasMaxLength(((IsLookup)c.ClrPropertyInfo.DeclaringType.GetCustomAttributes(typeof(IsLookup), false).FirstOrDefault()).DescriptionLength)
      );

  }

The result is:

  CreateTable(
      "Lookups.TestCategories",
      c => new {
        ID = c.Int(nullable: false, identity: true),
        Description = c.String(nullable: false),
        DisplayOrder = c.Int(nullable: false),
        Active = c.Boolean(nullable: false),
        RowVersion = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
      })
      .PrimaryKey(t => t.ID);

So, the question is...why doesn't Description have its length set in the migration code? Is this even possible?

<gripe>If there was a way to debug add-migration, this would be much easier.</gripe>


Solution

  • I am pretty sure that DeclaringType will give you the type in which the property was declared, in your example the Lookup class is where the Description property is declared. Since Lookup doesn't have the IsLookup attribute, nothing gets set. Try looking through the registered types first then setting the Description column after you find it:

    modelBuilder.Types()
        .Where(t => t.IsSubclassOf(typeof(Lookup)))
        .Having(x => x.GetCustomAttributes(false).OfType<IsLookup>().FirstOrDefault())
        .Configure((config, att) => {
            config.Property("Description").HasMaxLength(att.DescriptionLength);
        });