Shortcut in Fluent API to set multiple properties as required
Asked Answered
P

4

10
  1. Here is what I am currently doing:

    modelBuilder.Entity<Product>().Property(e => e.Name).IsRequired();                   
    modelBuilder.Entity<Product>().Property(e => e.UPC).IsRequired();      
    modelBuilder.Entity<Product>().Property(e => e.Price).IsRequired();      
    modelBuilder.Entity<Product>().Property(e => e.Description).IsRequired();
    
  2. Here is what I would like to be doing:

    modelBuilder.Entity<Product>()
        .Property(e => e.Name).IsRequired()
        .Property(e => e.UPC).IsRequired()
        .Property(e => e.Price).IsRequired()
        .Property(e => e.Description).IsRequired()
    

The latter, though, doesn't work. Is there another way not to have to repeat the modelBuilder.Entity<Product>() each time?

  1. Here is the current most pithy option:

    var e = modelBuilder.Entity<Product>();
    e.Property(e => e.Name).IsRequired();                   
    e.Property(e => e.UPC).IsRequired();      
    e.Property(e => e.Price).IsRequired();      
    e.Property(e => e.Description).IsRequired();
    
Pomiculture answered 25/8, 2013 at 1:46 Comment(3)
Not that i know of. Your first example is the only way AFAIKSexpartite
I know there are some limitations on the chaining using the fluent API but cannot remember exactly what they are. Looking at this documentation I cannot see any example where .Property is used twice for one entity msdn.microsoft.com/en-gb/data/jj591617Santoro
resharper templates come to mindPeatroy
E
7

This is compatible with all of the existing DbModelBuilder extension methods since it's just adding a fluent layer on top, but that it does come with some syntactical overhead. Not exactly what you asked for, but doesn't involve mucking around with the supporting code. Haven't fully tested this yet, but it should work if you're comfortable with the syntax:

// First option - like this better because it has less cruft than multiple Has invocations

var modelBuilder = new DbModelBuilder();
var modelConfiguration = new ModelConfigurator(modelBuilder);

modelConfiguration.Entity<Product>().Has(e => {
                                         e.Property(en => en.Name).IsRequired();
                                         e.Property(en => en.UPC).IsRequired();
                                         e.Property(en => en.Price).IsRequired();
                                         e.Property(en => en.Description).IsRequired();}
                                        );           

OR

var modelBuilder = new DbModelBuilder();
var modelConfiguration = new ModelConfigurator(modelBuilder);
modelConfiguration.Entity<Product>().Has(e => e.Property(en => en.Name).IsRequired())
                                    .Has(e => e.Property(en => en.UPC).IsRequired())
                                    .Has(e => e.Property(en => en.Price).IsRequired())
                                    .Has(e => e.Property(en => en.Description).IsRequired());

// continue configuring properties, and creating methods on ModelConfigurator as needed

Supporting code:

  public class Product{
        public string Name {get;set;}
        public double Price {get;set;}
        public string UPC {get;set;}
        public string Description {get;set;}

    }

    public class ModelConfigurator{

        public DbModelBuilder ModelBuilder{get;set;}

        public ModelConfigurator(DbModelBuilder modelBuilder){
            ModelBuilder = modelBuilder;
        }

        public EntityConfigurator<TEntity> Entity<TEntity>() where TEntity : class {
            var entity = ModelBuilder.Entity<TEntity>();
            return new EntityConfigurator<TEntity>(entity);
        }
    }

    public class EntityConfigurator<TEntity> where TEntity : class{

        public EntityTypeConfiguration<TEntity> EntityTypeConfiguration {get;set;}

        public EntityConfigurator(EntityTypeConfiguration<TEntity> entityTypeConfiguration){
            EntityTypeConfiguration = entityTypeConfiguration;
        }

        public EntityConfigurator<TEntity> Has(Action<EntityTypeConfiguration<TEntity>> a){
            a(this.EntityTypeConfiguration);
            return this;
        }
    }
Enrichetta answered 25/8, 2013 at 3:7 Comment(2)
That works and so I marked it as the answer. Due to it's syntactical overhead, though, I will probably keep using the standard Fluent API, because the third option in my original question will be easy for other developers to understand while also being reasonably pithy.Pomiculture
I think that makes the most sense, unless you want to spend a good amount of time redoing the existing fluent interface by overriding a lot of the internals.Enrichetta
D
4

Another option, no real need for Has() on top of Entity():

modelConfiguration.Entity<Product>(e => {
                                   e.Property(en => en.Name).IsRequired();
                                   e.Property(en => en.UPC).IsRequired();
                                   e.Property(en => en.Price).IsRequired();
                                   e.Property(en => en.Description).IsRequired();}
                                  );

Extension method:

public static EntityTypeConfiguration<TEntity> Entity<TEntity>(this DbModelBuilder modelBuilder, Action<EntityTypeConfiguration<TEntity>> action) where TEntity : class
{
    var r = modelBuilder.Entity<TEntity>();
    action(r);
    return r;
}
Daye answered 25/3, 2014 at 12:39 Comment(0)
V
2

I adapted jnm2's answer for EF Core:

public static class ModelBuilderExtensions
{
    public static EntityTypeBuilder<T> Entity<T>(
        this ModelBuilder modelBuilder,
        Action<EntityTypeBuilder<T>> action) where T : class
    {
        var e = modelBuilder.Entity<T>();
        action(e);
        return e;
    }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>(b =>
    {
        b.Property(e => e.Name).IsRequired();
        b.Property(e => e.UPC).IsRequired();
        b.Property(e => e.Price).IsRequired();
        b.Property(e => e.Description).IsRequired();}
    });
}
Void answered 11/11, 2021 at 17:21 Comment(0)
E
0

I guess you can do the following, although I think this is rather awkward.

public static class EntityConfigExtensions
{
    public static EntityTypeConfiguration<TEntity> Prop<TEntity, TProp>(this EntityTypeConfiguration<TEntity> self, Expression<Func<TEntity, TProp>> propExpression) where TEntity : class
    {
        self.Property(propExpression);
        return self;
    }
    public static EntityTypeConfiguration<TEntity> RequiredProp<TEntity, TProp>(this EntityTypeConfiguration<TEntity> self, Expression<Func<TEntity, TProp>> propExpression) where TEntity : class
    {
        self.Property(propExpression).IsRequired();
        return self;
    }
    // etcetera for other frequently used configs
    // ...
    // And, borrowing from David: a catch-all for the rest
    public static EntityTypeConfiguration<TEntity> Configure<TEntity, TProp>(this EntityTypeConfiguration<TEntity> self, Action<EntityTypeConfiguration<TEntity>> configAction) where TEntity : class
    {
        configAction(self);
        return self;
    }
}

Usage:

modelBuilder.Entity<Product>()
    .Prop(e => e.Name)
    .RequiredProp(e => e.UPC)
    .RequiredProp(e => e.Price)
    .Configure(x => x.Ignore(e => e.Description));
Elinorelinore answered 25/8, 2013 at 2:18 Comment(9)
I am receiving the error that, "The 'new()' constraint cannot be used with the 'struct' contraint."Pomiculture
You are right, needs to be removed. Funny because MSDN docs state: public PrimitivePropertyConfiguration Property<T>( Expression<Func<TStructuralType, T>> propertyExpression ) where T : struct, new()Elinorelinore
@ShaunLuttin If MSDN docs are not correct, you may also want to try without the where TProp : struct constraint.Elinorelinore
I now receive the following error: The type 'TEntity' must be a reference type in order to use it as parameter 'TEntityType' in the generic type or method.Pomiculture
@ShaunLuttin yes, as I indicated in the previous comment, you may also want to remove the where TProp: struct constraint and add where TEntity : class. The MSDN docs (msdn.microsoft.com/en-us/library/gg671207(v=vs.103).aspx) appear to be incorrect. I have edited the answerElinorelinore
The type 'TProp' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method.Pomiculture
It doesn't work for me. The code builds until I try to implement usage as follows: modelBuilder.Entity<Product>().RequiredProp(e => e.Name); Then I receive the following error: The type 'string' must be a non-nullable value type in order to use it as parameter 'TProp' in the generic type or method.Pomiculture
@ShaunLuttin did you try the version withouth the where TProp : struct clause?Elinorelinore
let us continue this discussion in chatElinorelinore

© 2022 - 2024 — McMap. All rights reserved.