Can I add a discriminator via a custom code first convention? - entity-framework

I add a discriminator column to all my entities in order to facilitate soft delete.
Currently I do it by specifying it on each entity one by one:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));
modelBuilder.Entity<Bar>().Map(m => m.Requires("IsDeleted").HasValue(false));
//etc etc
}
What I'd like to be able to do is specify it as a Custom Code First Convention. My entities all inherit from a ModelBase class. So I can create a custom convention to map to stored procedures like this:
modelBuilder.Types<ModelBase>().Configure(m => m.MapToStoredProcedures());
but this is not available:
modelBuilder.Types<ModelBase>().Configure(m => m.Requires("IsDeleted").HasValue(false));
So, is there any way to add a discriminator to all entities that inherit from ModelBase other than doing it one by one?

Related

Can I add a primary key and a unique constraint in EFCore

I'm manually moving from .NET Framework to .NET Core and working on the EF and DTOs.
I'm not enjoying this - I have read indexes are not supported via annotation and as such, am currently mixing fluent api and annotations which is a code smell. However, it appears as if I must proceed with this combination.
My question is if I can achieve this only with fluent api. My table has both a primary key and a unique constraints.
My object looks like
class Person
{
public int Id {get;set;}
public string UniqueToken {get;set;}
}
However, I am unable to add the following
modelBuilder.Entity<Person>()
.HasKey(a => a.Id)
.HasIndex(a => a.UniqueToken).IsUnique(); //this is what I would like to add but I can't.
I've attempted something which feels like a hacky work around
modelBuilder.Entity<Person>()
.HasKey(a => a.Id);
modelBuilder.Entity<Person>()
.HasIndex(a => a.UniqueToken).IsUnique();
Again, adding this entry twice seems a little bleugh… Fluent appears to want to simply chain the methods.
I have read on this, but I'm getting very lost. Is it possible to add both the primary key and unique constraint ?
Better you separate your entity configurations from OnModelCreating method with IEntityTypeConfiguration interface as follows:
public class PersonConfiguration : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(a => a.Id);
builder.HasIndex(a => a.UniqueToken).IsUnique();
}
}
Now you can add configuration for all of your Db entities like PersonConfiguration and register all of them at once as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PersonConfiguration).Assembly);
}
This will give you more separation of concern and readability!

Working with Schemas in EF Code First Migrations

I suppose this question is a cosmetic one; when you initially create an EF migration, it puts the schema in by default; for example:
public override void Up()
{
DropPrimaryKey("dbo.MyTable");
AddPrimaryKey("dbo.MyTable", "NewField");
This seems fine, unit you see the key name that it generates as a result (it has dbo in the key name).
I realise that one way around this is to specify the key name directly. Are there any other options, for example, can the schema be specified for a block, but not included in the specific modifications? For example:
public override void Up()
{
UseSchema("dbo");
DropPrimaryKey("MyTable");
AddPrimaryKey("MyTable", "NewField");
I realise that you can simply omit the schema name; i.e., this will work:
public override void Up()
{
DropPrimaryKey("MyTable");
AddPrimaryKey("MyTable", "NewField");
But how would I then deal with a situation where there were more than a single schema?
You can specify default schema using HasDefaultSchema method on DbModelBuilder class instance.
modelBuilder.HasDefaultSchema("schemaName");
You can also set schema for each entity using ToTable method on EntityTypeConfiguration<TEntityType> class instance. Which will generate migration scripts with provided schema for desired entity/ies.
modelBuilder.Entity<TEntity>().ToTable("tableName", "schemaName")
You can also use Table attribute to set schema for entity.
[Table("tableName","schemaName")]
Or you can write your own custom convention
public class DynamicSchemaConvention : Convention
{
public CustomSchemaConvention()
{
Types().Configure(c => c.ToTable(c.ClrType.Name, c.ClrType.Namespace.Substring(c.ClrType.Namespace.LastIndexOf('.') + 1)));
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new CustomSchemaConvention());
}
Related links:
DbModelBuilder.HasDefaultSchema Method
EntityTypeConfiguration.ToTable Method
TableAttribute Class
Entity Framework 6 - Code First: table schema from classes' namespace
Entity Framework Custom Code First Conventions (EF6 onwards)

Change MigrationsHistoryTable column names in EF Core

I have a standardized all table and column names in my EF Core database to use snake_case. I was able to change the migrations history table name and schema to match the rest of the database, but I am not able to find a way to change the columns from MigrationId to migration_id and ProductVersion to product_version.
Any ideas on how this could be done?
Here is an example of how to do it on SQL Server.
First, create a custom implementation of SqlServerHistoryRepository overriding ConfigureTable.
class MyHistoryRepository : SqlServerHistoryRepository
{
public MyHistoryRepository(
IDatabaseCreator databaseCreator, IRawSqlCommandBuilder rawSqlCommandBuilder,
ISqlServerConnection connection, IDbContextOptions options,
IMigrationsModelDiffer modelDiffer,
IMigrationsSqlGenerator migrationsSqlGenerator,
IRelationalAnnotationProvider annotations,
ISqlGenerationHelper sqlGenerationHelper)
: base(databaseCreator, rawSqlCommandBuilder, connection, options,
modelDiffer, migrationsSqlGenerator, annotations, sqlGenerationHelper)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property(h => h.MigrationId).HasColumnName("migration_id");
history.Property(h => h.ProductVersion).HasColumnName("product_version");
}
}
Then replace the replace the service with your custom implementation.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.UseSqlServer(connectionString)
.ReplaceService<SqlServerHistoryRepository, MyHistoryRepository>();

EF7 Identity custom table name first seed

I have created a new clean asp.net 5 project (rc1-final), I just need to change default ef identity table name.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
// On event model creating
base.OnModelCreating(builder);
// Define table name
builder.Entity<IdentityUser>().ToTable("BackEnd_AspNetUsers").Property(p => p.Id).HasColumnName("UserId");
builder.Entity<ApplicationUser>().ToTable("BackEnd_AspNetUsers").Property(p => p.Id).HasColumnName("UserId");
builder.Entity<IdentityUserRole<string>>().ToTable("BackEnd_AspNetUserRoles");
builder.Entity<IdentityUserLogin<string>>().ToTable("BackEnd_AspNetUserLogins");
builder.Entity<IdentityUserClaim<string>>().ToTable("BackEnd_AspNetUserClaims");
builder.Entity<IdentityRole>().ToTable("BackEnd_AspNetRoles");
}
}
I get following error
InvalidOperationException: Cannot use table 'BackEnd_AspNetUsers' in schema '' for entity 'ApplicationUser' since it is being used for another entity.
These lines here show you are trying to setup mappings for both the base identity classes and your application's inherited version of these classes;
builder.Entity<IdentityUser>().ToTable("BackEnd_AspNetUsers").Property(p => p.Id).HasColumnName("UserId");
builder.Entity<ApplicationUser>().ToTable("BackEnd_AspNetUsers").Property(p => p.Id).HasColumnName("UserId");
You don't need both, you should only have the inherited one specified - ApplicationUser.
In your case you should use ForSqlServerToTable("newtablename")
builder.Entity<IdentityUser>().ForSqlServerToTable("BackEnd_AspNetUsers").Property(p => p.Id).HasColumnName("UserId");
builder.Entity<ApplicationUser>().ForSqlServerToTable("BackEnd_AspNetUsers").Property(p => p.Id).HasColumnName("UserId");

Ignore Entities by Default

I have an existing database, to which I'd like to add Entity Framework mappings for just a handful of tables/entities. Is there a way to ignore all entities by default, and then selectively include them?
I have this in the context constructor to not migrate changes:
Database.SetInitializer(new NullDatabaseInitializer<Context>());
And then I have the following fluent code to map the existing entities:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Cube>()
.Map(e => e.ToTable("tblCubes"))
.HasKey(e => e.CubeId);
...
However, when I run any EF queries, I get the error:
One or more validation errors were detected during model generation.
EntityType 'xyz' has no key defined. Define the key for this EntityType
Rather than using modelBuilder.Ignore<xyz>(); on every existing and future entity, can't I just get EF to ignore all by default, and only map those I choose/include?
EDIT============
One of my EF entities (CubeFact) has relational properties to other classes like this one below to the Year class:
private Year _year;
public Int16 YearId { get; set; }
public Year Year { get { return _year ?? (_year = Year.GetYearById(YearId)); } set { _year = value; } }
The Year class then links to a Fact class, which is one of the classes failing validation. But neither the Year class nor the Fact class have been explicitly mapped. Does EF follow these relationships and then validate, even if I haven't explicitly told it about the relationships?