I have a database that I have inherited and would like to turn the database itself into migrations that I can use in an ASP.NET Core 3 API. I would like to, if I can, to not have to write the migrations by hand and rather, turn the database into migrations.
I understand that this is very easy with Entity Framework Core, but I wish to use dapper, not EF due to performance reasons as well as personal preference.
I have explored and have come into contact with two libraries for migration management compatible with Postgres: Fluent Migrator and Evolve.
Does anyone have any experience with either of these in the generation of migrations from an existing database?
Thanks,
I am the maintainer of FluentMigrator. I've never heard of Evolve, but other frameworks I've seen include db-up, RoundhousE, and SharpMigrations. Since FluentMigrator has been around for over a decade, it likely has the most complete out-of-the-box feature set of any database migration framework. I previously used RoundhousE but found it caused more problems than it solved.
FluentMigrator does not "generate" migrations for you. You have to write them yourself. By comparison, Entity Framework Core can use its fluent model building syntax and automatically infer up/down migrations, and you can focus primarily on writing data migrations.
That said, here is how Microsoft recommends writing migrations with EFCore, compared with the same logic in FluentMigrator:
EFCore
Source: https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/migrations?view=aspnetcore-3.1#examine-up-and-down-methods
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Course",
columns: table => new
{
CourseID = table.Column<int>(nullable: false),
Credits = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Course", x => x.CourseID);
});
// Additional code not shown
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Enrollment");
// Additional code not shown
}
}
FluentMigrator
[Migration(1, "InitialCreate")]
public class InitialCreate : Migration
{
public void Up()
{
Create.Table("Course")
.WithColumn("CourseID").AsInt32().NotNullable().PrimaryKey()
.WithColumn("Credits").AsInt32().NotNullable()
.WithColumn("Title").AsString().Nullable();
// Additional code not shown
}
protected override void Down(MigrationBuilder migrationBuilder)
{
Delete.Table("Enrollment");
// Additional code not shown
}
}
In the above example, you don't need to type out PK_Course in your primary key definition, because FluentMigrator supports "Don't Repeat Yourself" principle via naming conventions and "knows" that is the correct name for your primary key. You just define your naming conventions once.
Also, because of the fluent syntax, you'll find you have to type a lot less than you do with EFCore migrations. If you find this to not be the case, please provide feedback and we will make it more efficient.
Related
I am upgrading a web app from Asp.Net Core 2.0 to .Net 6. This application uses Entity Framework Core 6 and PostgreSQL 12 DB. Application has a context class named ApplicationDbContext which is setup in following manner.
Setup -
services.AddDbContext<ApplicationDbContext>(options => options.UseNpgsql(
new Npgsql.NpgsqlConnection(App.Constants.PostgresConnectionString),
(opts) => { opts.MigrationsAssembly(_migrationsAssembly); opts.CommandTimeout(COMMAND_TIMEOUT); }),
ServiceLifetime.Transient);
Upon using Equals() method overload with StringComparison parameter to perform equality check an exception is raised stating the operation could not be translated, e.g.
_dbContext.Users.Any(t => t.Email.Equals(user.Email, StringComparison.OrdinalIgnoreCase));
I found that case insensitive search is not supported on EF Core 6.0 and its left up to DB collation, I found alternative approaches to achieve the required outcome here
(EF Core postgresql string compare (case-insensitive)). However apart from setting up collation on database, other options are not feasible to the current use case.
In order to setup collation on the database I modified my context class using this documentation (https://www.npgsql.org/efcore/misc/collations-and-case-sensitivity.html?tabs=data-annotations), which turned my context class to look like this -
public class ApplicationDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.HasCollation("case_insensitive_collation", locale: "en-u-ks-primary", provider: "icu", deterministic: false);
base.OnModelCreating(builder);
}
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<string>().UseCollation("case_insensitive_collation");
}
}
and the corresponding db code was modified to -
_dbContext.Users.Any(t => t.Email.Equals(user.Email))
After performing these modifications in code to setup case insensitive collation, the database is still performing case sensitive equality comparison. i.e. it does not distinguish between user email abc#company.com and ABC#company.com.
I have following queries -
Am I setting up collation incorrectly?
How can I achieve case insensitive equality/search with this setup (without creating a new db/indexes)?
Note - The database already exists, additionally it's not feasible to recreate the db/indexes.
I am trying to add a UNIQUE constraint to the "Username" property of my "UserAccount" entity/class. With code-first, that would be no problem, but for model-first, I can't find anything on how to achieve that.
The designer does not support this feature. I cannot use annotations because the entity classes are auto-generated. I cannot use Fluent API because the OnModelCreating() method is not called in model-first and thus I have no DbModelBuilder instance.
The only thing I can think of is executing some kind of manual SQL statement at application start that creates the UNIQUE constraint, which kind of defeats the purpose of EF.
Here is my current DbContext class:
public partial class UserAccountsModelContainer : DbContext
{
public UserAccountsModelContainer()
: base("name=UserAccountsModelContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<UserAccount> UserAccounts { get; set; }
}
I won't even bother to post the UserAccount class since it's auto-generated and shouldn't be modified (I know that the DbContext is also auto-generated, but modifying it is possible).
Any help on this is appreciated!
First I will recommend you to switch to Entity Framework Code First, too. It gives you much more controll about every thing that is possible with EF.
I never used it before, but I know Model Conventions. They are applicable to the model configuration. Maybe it will be an approach to set up a convention for a defined model type/property that should be configured as unique constraint.
Based on the following it should be possible to modify the set up of model first on creating database.
Model Conventions are based on the underlying model metadata. There
are conventions for both CSDL and SSDL. Create a class that implements
IConceptualModelConvention from CSDL conventions and implement
IStoreModelConvention for SSDL convention.
Source: http://www.entityframeworktutorial.net/entityframework6/custom-conventions-codefirst.aspx
There are two types of model conventions, Conceptual (C-Space) and
Store (S-Space). This distinction indicates where in the pipeline a
convention is executed. A C-Space convention is applied to the model
that the application builds, whereas an S-Space convention is applied
to the version of the model.
Source: https://entityframework.codeplex.com/wikipage?title=Custom%20Conventions
Some more example implementations incl. explainations are findabe on msdn. I guess they are very helpful for your case.
One example from MSDN:
public class DiscriminatorRenamingConvention : IStoreModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
if (property.Name == "Discriminator")
{
property.Name = "EntityType";
}
}
}
It will rename the column Discriminator into EntityType. It is a very simple example but you could modify it to solve your problem to:
public class ModelBasedConvention : IConceptualModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
if (property.Name == "Username"
&& property.DeclaringType.GetType() == typeof(UserAccount))
{
property.AddAnnotation("UniqueKey", property);
}
}
}
I'm looking for information about using entity framework with an existing database, but to keep my poco classes in another library.
I've done this a number of times in the past, but I've always ended up with my model classes in my data access library using EF and my domain classes in a separate library. Inevitably this meant writing code to translate between my domain classes and my model classes. This seems pointless and inefficient since the classes are usually almost identical.
Can anyone point me to a walkthrough keeping my classes are materialized by EF in a separate library? I would need to be able to do some minor name correction (eg Filter_Rule --> FilterRule). I would also like to be able to keep anything EF specific in the data access library so that I can swap out the data access library if I need to.
Thanks,
Jason
This should be quite straightforward. Create a DbContext code-first style as normal, adding DbSets and configurations as necessary to tell EF about your database. Set your initializer to null so it doesn't try to mess with your existing database, and voila...
public class YourContext : DbContext
{
public DbSet<YourPoco> YourPocos { get; set; }
static YourContext()
{
Database.SetInitializer<YourContext>(null);
}
public YourContext() : base("database_name")
{
}
protected override void OnModelCreating(DbModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<YourPoco>().Property(x => x.FilterRule).HasColumnName("Filter_Rule");
//OR
builder.Configurations.Add(new YourPocoConfig());
//OR
builder.Configurations.AddFromAssembly(typeof (YourContext).Assembly);
}
}
public class YourPocoConfig : EntityTypeConfiguration<YourPoco>
{
public YourPocoConfig()
{
HasKey(x => x.Id);
Property(x => x.FilterRule).HasColumnName("Filter_Rule");
}
}
If you are worried about getting everything to match your database structure, you can use Entity Framework Tools for Visual Studio to reverse engineer your models, then match the configuraiton or copy the generated POCO's into your other library and convert the data annotations into respective EntityTypeConfiguration classes to keep the POCO's clean.
MSDN document on reverse engineering code-first.
I see many examples of seeding with Code First, but I'm not sure I understand what the idiomatic way of seeding the database when using EF Database First.
Best practice is very situation dependent. Then there is the DEV versus PROD environments.
Auto seed when using Drop and recreate on model change during DEV so you have test data makes the most sense. This is when it used most.
Of cause you can a have a test method that you trigger manually. I personally find the idea an automatically triggered seed method not that exciting and more for DEV prototyping when the DB structure is volatile. When using migrations, you tend to keep your hard earned test data. Some use Seeding during initial installation in PROD. Others will have a specific load routines triggered during the installation/commissioning process. I like to use custom load routines instead.
EDIT: A CODE FIRST SAMPLE. With DB First you just write to the Db normally.
// select the appropriate initializer for your situation eg
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, MyMigrationConfiguration>());
Context.Database.Initialize(true); // yes now please
//...
public class MyMigrationConfiguration<TContext> : DbMigrationsConfiguration<TContext>
where TContext : DbContext{
public MyMigrationConfiguration() {
AutomaticMigrationsEnabled = true; //fyi options
AutomaticMigrationDataLossAllowed = true; //fyi options
}
public override void Seed(TContext context)
{
base.Seed(context);
// SEED AWAY..... you have the context
}
}
Is it possible to map a table column to a class field instead to a class property and how?
YOU CAN DO IT :)
Follow this link: http://weblogs.asp.net/ricardoperes/archive/2013/08/22/mapping-non-public-members-with-entity-framework-code-first.aspx
This is a common request, and really makes sense; we need to use LINQ expressions and a bit of reflection magic. First, an helper function for returning an expression that points to a member:
public static class ExpressionHelper
{
public static Expression<Func<TEntity, TResult>> GetMember<TEntity, TResult>(String memberName)
{
ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "p");
MemberExpression member = Expression.MakeMemberAccess(parameter, typeof(TEntity).GetMember(memberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Single());
Expression<Func<TEntity, TResult>> expression = Expression.Lambda<Func<TEntity, TResult>>(member, parameter);
return (expression);
}
}
Then, we call it on the DbContext.OnModelCreating method, as a parameter to StructuralTypeConfiguration.Property:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>().Property(ExpressionHelper.GetMember<Project, Decimal>("Budget")).IsRequired();
base.OnModelCreating(modelBuilder);
}
Entity Framework (Code First or not) does not support mapping to a field; only to properties.
UPDATE
As pointed out in the comments, these documents are a bit dated but might still help any beginner along:
Entity Framework Code first development Resources and Documentation
For the sake of completeness, heres a link to whats included in EF 4.1 RC: EF 4.1 Release Candidate Available
Changes since CTP5 (From the link above):
Rename of ‘DbDatabase’ to ‘Database’. This class has also moved
to the ‘System.Data.Entity’
namespace, along with the database
initializer classes.
Rename of ‘ModelBuilder’ to ‘DbModelBuilder’, to align with the
other core classes.
Validation in Model First and Database First. The new validation
feature was only supported in Code
First in CTP5. In RC the validation
feature will work with all three
development workflows (Model First,
Database First, and Code First).
Complete Intellisense docs. Feature CTPs were not extensively documented
because the API surface was changing
significantly between each release.
This release includes complete
documentation.
Removal of Code First Pluggable Conventions. Pluggable Conventions
were previewed in Feature CTP5 but
were not at go-live quality for this
release. This release still supports
the removal of default conventions.
Consolidation of IsIndependent in the Code First relationship API. When
configuring relationships in Feature
CTP5 the IsIndependent method was used
to identify that the relationship did
not have a foreign key property
exposed in the object model. This is
now done by calling the Map method.
HasForeignKey is still used for
relationships where the foreign key
property is exposed in the object
model.