Errors with tables creation by code first model with npgsql

不羁的心 提交于 2019-12-12 00:43:37

问题


Problem with tables creation by code first model with npgsql

Database creation should work from version 2.2.0-beta1: «David Karlaš added support for EFMigration and Database creation in EF6+ Now it is possible to start Code First projects without needing to create a database upfront. EntityFramework and Npgsql will take care of it. Emil Lenngren added support for a lot of missing features of EntityFramework.» https://www.nuget.org/packages/Npgsql/2.2.0-beta1

But, when I tried to do this I faced with problems.

Firstly, I did the simple project, that works with SqlServer: I created Asp.Net MVC project (VS2013) and add some code:

public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; }
}

public class MovieDBContext : DbContext
{
    public DbSet<Movie> Movies { get; set; }
}

public class MovieDBInitializer : DropCreateDatabaseIfModelChanges<MovieDBContext>
{
}

public class HomeController : Controller
{
    private MovieDBContext db = new MovieDBContext();

    public ActionResult Index()
    {
        List<Movie> objs = db.Movies.ToList();
        return View(objs);
    }

WebConfig:

  <connectionStrings>
    <add name="MovieDBContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MovieCreateDbInSql;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MovieDB.mdf" providerName="System.Data.SqlClient" />  
  </connectionStrings>

  <entityFramework>
    <contexts>
      <context type="MovieCreateDbInSql.Models.MovieDBContext, MovieCreateDbInSql">
        <databaseInitializer type="MovieCreateDbInSql.Models.MovieDBInitializer, MovieCreateDbInSql" />
      </context>
    </contexts>

    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>

When starting the project the database MovieDB is created.

All is works.

We can see this database in App_Data folder in the project. Good.

Then I tried to do the same with npgsql.

  1. Add libs:

EntityFramework6.Npgsql.dll (version 3.1.0.0)

Npgsql.dll (version 3.1.2.0)

2.

Than change WebConfig:

  <connectionStrings><add name="MovieDBContext" connectionString="Server=127.0.0.1;Port=5432;Database=postgres;User Id=postgres;Password=postgres000;" providerName="Npgsql" /></connectionStrings>

  <entityFramework>
    <contexts>
      <context type="MovieCreateDbInSql.Models.MovieDBContext, MovieCreateDbInSql">
        <databaseInitializer type="MovieCreateDbInSql.Models.MovieDBInitializer, MovieCreateDbInSql" />
      </context>
    </contexts>
    <defaultConnectionFactory type="Npgsql.NpgsqlFactory, Npgsql" />
    <providers>
      <provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, EntityFramework6.Npgsql"></provider>
    </providers>
  </entityFramework>

  <system.data>
    <DbProviderFactories>
      <remove invariant="Npgsql" />
      <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql"
           type="Npgsql.NpgsqlFactory, Npgsql" />
    </DbProviderFactories>
  </system.data>

Start. Error:

System.NotSupportedException was unhandled by user code
  HResult=-2146233067
  Message=Model compatibility cannot be checked because the database does not contain model metadata. Model compatibility can only be checked for databases created using Code First or Code First Migrations.
  Source=EntityFramework

But this configuration was enough for SqlServer!

Ok. Try this:

EF5 Getting this error message: Model compatibility cannot be checked because the database does not contain model metadata

1.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}

Don't really help. The error is the same

  1. public class MovieDBInitializer : DropCreateDatabaseAlways { }

New error:

Npgsql.PostgresException was unhandled by user code
  HResult=-2147467259
  Message=55006: база данных "postgres" занята другими пользователями
  Source=Npgsql
  ErrorCode=-2147467259
  BaseMessage=база данных "postgres" занята другими пользователями
  Code=55006
  Detail=Эта база данных используется ещё в 1 сеансе.
(error 55006 database is being accessed by other user)

This error is not good too. As far as I understand this error is because we have serious database posgresql unlike primitive localdb.sql. And dropping db operation in postgresql is not so easy as in localdb.sql.

I found several links on that error:

https://groups.google.com/forum/#!topic/npgsql-help/1f5niOiHpGg

Drop a database being accessed by another users?

  1. npgsql and Entity Framework code first setup problems

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer<MovieDBContext>(null);
    }
    

The same error again:

System.NotSupportedException was unhandled by user code
  HResult=-2146233067
  Message=Model compatibility cannot be checked because the database does not contain model metadata. Model compatibility can only be checked for databases created using Code First or Code First Migrations.
  Source=EntityFramework

What should I do to have the opportunity to create tables by the code first model?

Of course I can generate the database in SqlServer and than convert scripts to postgresql, but I want do this with npgsql.


回答1:


Libraries that I use:

  • EntityFramework.6.1.3
  • EntityFramework6.Npgsql.3.1.0
  • Npgsql.3.1.3

My Database context:

public class CustomContext : DbContext
{

    public CustomContext()
        : base("name=CustomContext")
    {
        Database.SetInitializer(
            new MigrateDatabaseToLatestVersion<CustomContext, Configuration>());
    }

    public DbSet<Movie> Movies { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Change schema name from "dbo" => "public" for all entities (except MigrationHistory).
        // MigrationHistory schema name is modified in custom HistoryContext 
        modelBuilder.Types().Configure(c => c.ToTable(c.ClrType.Name, "public"));
    }

}

Migration history table has a default schema set to "dbo". If you want to have it in a different schema (ex. public) you have to create a custom HistoryContext.
Currently it's impossible to set a default schema with modelBuilder.HasDefaultSchema("public") while using automatic migrations (AFAIK).

public class CustomHistoryContext : HistoryContext
{
    public CustomHistoryContext(DbConnection existingConnection, string defaultSchema) 
       : base(existingConnection, defaultSchema)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Change schema from "dbo" to "public"
        modelBuilder.Entity<HistoryRow>()
                    .ToTable(tableName: "__MigrationHistory", 
                             schemaName: "public");
    }
}

Finally, my migration configuration class looks like this:

internal sealed class Configuration : DbMigrationsConfiguration<CustomContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;

        // Setting custom historyContext factory.
        // Used for modify __MigrationHistory table schema name from "dbo" to "public"
        this.SetHistoryContextFactory("Npgsql",
            (connection, defaultSchema) => 
                 new CustomHistoryContext(connection, defaultSchema));

    }

    protected override void Seed(CustomContext context)
    {
        //  This method will be called after migrating to the latest version.
    }
}

After these steps automatic code first migrations work correctly.




回答2:


However, here is a bit more details.

I have classes:

public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime IssueDay { get; set; }
public decimal EarnedMoney { get; set; }
public virtual ICollection<Actor> Actors { get; set; }
}
public class Actor
{
public int ID { get; set; }
public int MovieID { get; set; }
public DateTime BirthDay { get; set; }
public string Name { get; set; }
}

And I got scripts:

CREATE TABLE "Movie"
(
"ID" serial NOT NULL,
"Title" text,
"IssueDay" timestamp without time zone NOT NULL DEFAULT '0001-01-01 00:00:00'::timestamp without time zone,
"EarnedMoney" numeric(18,2) NOT NULL DEFAULT 0,
CONSTRAINT "PK_public.Movie" PRIMARY KEY ("ID")
)
WITH (
OIDS=FALSE
);
ALTER TABLE "Movie"
OWNER TO postgres;

CREATE TABLE "Actor"
(
"ID" serial NOT NULL,
"MovieID" integer NOT NULL DEFAULT 0,
"Name" text,
"BirthDay" timestamp without time zone NOT NULL DEFAULT '0001-01-01 00:00:00'::timestamp without time zone,
CONSTRAINT "PK_public.Actor" PRIMARY KEY ("ID"),
CONSTRAINT "FK_public.Actor_public.Movie_MovieID" FOREIGN KEY ("MovieID")
REFERENCES "Movie" ("ID") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
OIDS=FALSE
);
ALTER TABLE "Actor"
OWNER TO postgres;

CREATE INDEX "Actor_IX_MovieID"
ON "Actor"
USING btree
("MovieID");

And these scripts require additional processing of course.

That is why in general there is no difference generate scripts in postgres directly or generate in sqlServer and then convert to postgres by means of Navicat for example…



来源:https://stackoverflow.com/questions/37524540/errors-with-tables-creation-by-code-first-model-with-npgsql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!