0

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>

Bit Herder
  • 33
  • 7
  • I know it's annoying to question the rationale...but, why do you need to specify the length on the class-level attribute? Why not just put your arbitrary lengths on the code and description fields and have an IsLookup attribute on the class that doesn't take lengths? You can always grab the lengths from the attributes on the properties using reflection. – siride Jun 16 '16 at 13:45
  • Quite all right - I do over-engineer some things. The purpose is to build 40 or 50 lookups, each with a different description field length. That max length drives code generation for validation, otherwise I'd just set the length to 250 for everything and be done with it. – Bit Herder Jun 16 '16 at 13:48

1 Answers1

0

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);
    });
Kevin
  • 749
  • 5
  • 10
  • You, sir, are a genius! I was going after it from the wrong direction. I also had no idea about the method `IsSubclassOf()`. Can't thank you enough! – Bit Herder Jun 22 '16 at 14:51