Jim Reineri
Jim Reineri

Reputation: 2980

Cannot create EF code first mapping class using generics

I have several entites that are identical except for the class name that each are mapped to a corresponding identical table. The mapping for each table is similar to the following:

modelBuilder.Entity<Foo>().Map(x =>
{
  x.MapInheritedProperties();
  x.ToTable("Foo");
})

This approach works, but is repetitive.

I created this class hoping to get rid of the reposition. it is simplified here for brevity.

public class Generic<T>
{
    public Generic(DbModelBuilder modelBuilder, string tableName)
    {
        modelBuilder.Entity<T>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable(tableName);
        });
    }
}

I get the following compiler error that I do not understand:

The type 'T' must be a reference type in order to use it as parameter 'TEntityType' in the generic type or method 'System.Data.Entity.DbModelBuilder.Entity<TEntityType>()'

Thanks in advance, Jim

Upvotes: 1

Views: 2219

Answers (3)

Alexander Manekovskiy
Alexander Manekovskiy

Reputation: 3203

The error states that your generic lacks a class constraint. Read here about "Constraints on Type Parameters".

So Generic<T> should be declared as

public class Generic<T> where T: class
{
    public Generic(DbModelBuilder modelBuilder, string tableName)
    {
        modelBuilder.Entity<T>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable(tableName);
        });
    }
}

But I would suggest using EntityTypeConfiguration. This class will allow you to separate entity mappings from context and implement a kind of inheritance you wanted.

For example:

public abstract class EntityConfiguration<T> : EntityTypeConfiguration<T>
    where T : Entity
{
    protected EntityConfiguration()
    {
        ToTable(typeof(T).Name);

        // All primary keys are named as <EntityName>Id
        Property(e => e.Id)
            .HasColumnName(typeof(T).Name + "Id");
    }
}

This class states that all entities will have a mapping to table which name is equal to name of the type and every table has an primary key column with name <TableName>Id.

Then mapping configuration for entity Foo could be declared as following:

public class FooConfiguration : EntityConfiguration<Foo>
{
    public FooConfiguration()
    {
        Map(m => m.MapInheritedProperties());
        // add you mapping logic here
    }
}

Then configuration should be registered in DbContext:

public class MyDbContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new FooConfiguration());
    }
}

Upvotes: 3

w.brian
w.brian

Reputation: 17417

EF provides a class that allows you to do this:

class SomeEntityMapping : EntityTypeConfiguration<SomeEntity>
{
    public SomeEntityMapping()
    {
        ToTable("My_Entity");
        HasKey(e => e.Id);
        //...
    }
} 

Then, in your DbContext, override OnModelCreating and add the Mappings to the configuration:

protected override void OnModelCreating(DbModelBuilder builder)
{
   builder.Configurations.Add(new MyEntityConfiguration());
}

Upvotes: 0

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236308

Just add generic parameter constraint where T : class:

public class Generic<T>
   where T : class
{
    public Generic(DbModelBuilder modelBuilder, string tableName)
    {
        modelBuilder.Entity<T>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable(tableName);
        });
    }
}

Same constraint exist on DbModelBuilder.Entity<T> method, that's why you need same constraint in your generic class.

Upvotes: 4

Related Questions