Till now all my tables in the database were singular. After I added a new Entity (in the .edmx with the designer) and recompiled, all the tables were written in plural.
In the model browser: x.Database.Model.Store -> Tables / Views -> every single table is written in plural. But in: x.Database.Model -> Entity Types everything is still in singular.
I already tried following:
1: Override the OnModelCreating method (edited in the template)
System.Data.Entity.Database.SetInitializer(new CreateDatabaseIfNotExists<LISADbConfiguration>());
context.Database.Initialize(true);
public class LISADbConfiguration : LISADb
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
2: Set: Tools -> Options -> Database Tools -> O/R Designer -> Enabled = False
3: In the .edmx file directly in the properties: Pluralize New Objects = False
But still everything results in plural tables in the database / Model Browser.
Database: MSSQL 2014
EF: 6
Related
I'm setting up Entity Framework Core in a new API to deploy to an existing SQL Server database that is used by Entity Framework 4.6 applications. There is one Migration History table that is shared by other applications, and has 2 fields in it that need to be populated for each entry: ContextKey, and Model. Entity Framework Core does not have a Context Key, and does not save the Model to the Migration History table.
I've already created a HistoryRepository : SqlServerHistoryRepository and configured Entity Framework Core to use it, but the ConfigureTable method only allows you to create additional columns, but not actually populate each record as it gets inserted with custom data. Providing a default value to the column is not a solution.
public class HistoryRepository : SqlServerHistoryRepository
{
public HistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property<string>("ContextKey")
.HasMaxLength(300);
history.Property<byte[]>("Model");
}
}
services.AddDbContext<MDSContext>(options =>
options.UseLazyLoadingProxies()
.UseSqlServer(
connectionString,
x => x.MigrationsHistoryTable("__MigrationHistory")).ReplaceService<Microsoft.EntityFrameworkCore.Migrations.IHistoryRepository, Burkhart.CoreServices.IncomingOrders.Core.Models.Base.HistoryRepository>()
);
I should be able to provide a custom value for ContextKey and Model dynamically
I looked all over for solutions, but they all show you how to add a column and set a default value, but not how to set a value dynamically. I ended up digging into the ASP.NET Entity Framework Core source code at GitHub for the solution, so that I would share it with everyone else, as I know there are others that are looking for this information:
Just override the GetInsertScript method on the HistoryRepository and insert your custom values. Here is the full solution:
public class HistoryRepository : SqlServerHistoryRepository
{
public HistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property<string>("ContextKey")
.HasMaxLength(300);
history.Property<byte[]>("Model");
}
public override string GetInsertScript(HistoryRow row)
{
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));
return new StringBuilder().Append("INSERT INTO ")
.Append(SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema))
.Append(" (")
.Append(SqlGenerationHelper.DelimitIdentifier(MigrationIdColumnName))
.Append(", ")
.Append(SqlGenerationHelper.DelimitIdentifier(ProductVersionColumnName))
.Append(", [ContextKey], [Model])")
.Append("VALUES (")
.Append(stringTypeMapping.GenerateSqlLiteral(row.MigrationId))
.Append(", ")
.Append(stringTypeMapping.GenerateSqlLiteral(row.ProductVersion))
.Append($", '{ContextConstants.ContextName}.{ContextConstants.ContextSchemaName}', 0x)")
.AppendLine(SqlGenerationHelper.StatementTerminator)
.ToString();
}
}
Here is a link to the source code on github:
https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore.Relational/Migrations/HistoryRepository.cs
I have a EF core code first web api application.
There is a Products entity and a UserProductsRating child entity (with a one to many relationship)
I also wanted to have an average review score (and be able to select/sort based on it) so created a view to do this (using the method described in this answer
[https://stackoverflow.com/a/18707413][1])
So the migration for my View looks like:
protected override void Up(MigrationBuilder migrationBuilder)
{
string script =
#"
CREATE VIEW AverageProductRating AS
SELECT u.ProductId, AVG(CAST(u.Rating AS FLOAT)) as AverageRating
FROM dbo.UserRatings u GROUP BY u.ProductId";
migrationBuilder.Sql(script);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
string script = #"DROP VIEW dbo.AverageProductRating";
migrationBuilder.Sql(script);
}
Then there is an AverageRating entity on top.
This all works fine and allows me to create queries like:
var top5Products = _db.Products.Include(x => x.AverageProductRating)
.Where(x => x.AverageProductRating != null)
.OrderByDescending(x => x.AverageProductRating.AverageRating)
.Take(5);
The problem occurs when I get to my Unit/Integration Tests. I am using the InMemoryDatabase with EnsureCreated to set up a testing instance/seed data.
var options = new DbContextOptionsBuilder<ProductsContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.EnableSensitiveDataLogging()
.Options;
var context = new ProductsContext(options);
context.Database.EnsureCreated();
When I run tests against this the AverageProductRating Entity always has zero rows (I'm not sure if the View gets created at all or not)
I think this may be to do with restrictions in SQL in the inmemory db or the way migrations are run, but I'm not sure.
Any suggestions on how to work round this would be welcome
Thanks
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)
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?
I have a model with several entities in my MVC4 project with VS 2012. Recently I added a view to my DB named 'vwTeacherNames' and I tried to update the model and I unchecked the Plorizing option for that update.
Then, I rename my entity to 'TeacherName'. Now when I tun the Prj, this exception is thrown where I define a DropDownList for teachers:
Invalid object name 'dbo.TeacherNames'.
I tried many ways such as using custom tool, removing the .tt files and generating the again, ... However the problem stays firm!
So, how can I tell the EF the right table(in fact view) name which is vwTeacherNames?
Thanks a lot
Found it! and I add it here with some more tweaks:
public class myDbContext : DbContext
{
public PtDbContext()
: base("DefaultConnection")
{
}
... //some entities
//Here it is:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TeacherName>().Property(t => t.FullName)
.HasColumnName("TeacherName");
modelBuilder.Entity<TeacherName>().ToTable("vwTeacherNames", schemaName: "dbo");
}
}
Update: Why waisting your time by defining what you previously defined?! Just kill the default table naming convention and enjoy progressing your Prj:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Adding this line tells the EF not to go through that convention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
So, It should builds up your queries by EntitySetName and EntityName properties of your entities which the first of is the DB table name and the second is your entity name which you use in your DbContext.