1

I have many blocks of code that look like the following:

modelBuilder
    .Entity<Foo>()
    .Property(t => t.X)
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_X_Y", 1) { IsUnique = true }));

modelBuilder
    .Entity<Foo>()
    .Property(t => t.Y)
    .IsRequired()
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_X_Y", 2) { IsUnique = true }));

This block is telling EF, through fluent API, to create a unique index with columns X and Y together, on table Foo.

Another block of code just like that would be this, with columns R and S on table Bar:

modelBuilder
    .Entity<Bar>()
    .Property(t => t.R)
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_R_S", 1) { IsUnique = true }));

modelBuilder
    .Entity<Bar>()
    .Property(t => t.S)
    .IsRequired()
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_R_S", 2) { IsUnique = true }));

I want to refactor this, so that it ends up looking something like:

CreateCompositeUnique<Foo>(modelBuilder, "IX_X_Y", t => new {t.X, t.Y});
CreateCompositeUnique<Bar>(modelBuilder, "IX_R_S", t => new {t.R, t.S});

I was thinking of something like this:

private void CreateCompositeUnique<T>(DbModelBuilder modelBuilder, string indexName, List<Expression<Func<T, byte[]>>> properties)
{ 
    for (int i = 0; i < properties.Count; i++)
    {
        modelBuilder
        .Entity<typeof(T)>()
        .Property(properties[i])
        .IsRequired() 
        .HasMaxLength(60)  // --only when propery is string
        .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute(indexName, i) { IsUnique = true }));
    }
}

But I have some questions:

  1. Is this a good idea?
  2. How do I know if the property is string?
  3. What is the best way to pass the parameters?
  4. How do I access "T"? I'm getting a compile error at .Entity<typeof(T)>
sports
  • 7,851
  • 14
  • 72
  • 129
  • 1
    I wouldn't bother doing this. You will probably find yourself adding more and more parameters to this method to account for subtle differences between types. Since configuration is among the most stable parts of a code base (usually) I wouldn't mind some degree of repetition. You *could* use an [extension method](http://stackoverflow.com/q/24337523/861716) to streamline this clunky `HasColumnAnnotation` syntax a bit. Also, I wouldn't like a method that does more than its name suggests. `CreateCompositeUnique` shouldn't set constraints like max length. – Gert Arnold Oct 09 '14 at 19:52
  • I like the idea of the extension method – sports Oct 09 '14 at 20:09

1 Answers1

2

Following Gert Arnold's advice, I created a extension method. Actually they are two extension methods (why? autoexplicative, see code comments)

public static class ExtensionMethods
{
    public static StringPropertyConfiguration HasIndex(this StringPropertyConfiguration config, string indexName, int i)
    {
        return config.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation((new IndexAttribute(indexName, i) { IsUnique = true })));
    }

    public static PrimitivePropertyConfiguration HasIndex(this PrimitivePropertyConfiguration config, string indexName, int i)
    {
        return config.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation((new IndexAttribute(indexName, i) { IsUnique = true })));
    }
}

And usage is like this:

modelBuilder
    .Entity<Foo>()
    .Property(t => t.X)
    .IsRequired()
    .HasMaxLength(60)
    .HasIndex("IX_X_Y", 1); // <-- here (X is string)

modelBuilder
    .Entity<Foo>()
    .Property(t => t.Y)
    .IsRequired()
    .HasIndex("IX_X_Y", 2); // <-- and here (Y is a primitive)
sports
  • 7,851
  • 14
  • 72
  • 129