我正在努力使用 EF 6(针对 .NET 4.x)、代码优先和 SQLite。
我使用 X# 而不是 C#,但它只影响语法。
问题是未创建带有该表的 SQLite 数据库,因此
SaveChanges()
会抛出“DbUpdateException”异常,因为该表不存在(将使用 0 字节创建 db3 文件):
内部异常1: UpdateException:更新条目时发生错误。有关详细信息,请参阅内部异常。
内部异常2: SQLiteException:SQL逻辑错误
没有这样的表:博客
日志输出仅显示没有表
__MigrationHistory
但这应该不重要。
完整代码如下:
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Data.Entity
PUBLIC CLASS CustomIntializer INHERIT CreateDatabaseIfNotExists<BloggingContext>
PROTECTED OVERRIDE METHOD Seed(context AS BloggingContext) AS VOID
VAR Blogs := List<Blog>{} {;
Blog {} {Name := "Blog 1"},;
Blog {} {Name := "Blog 2"};
}
Blogs:ForEach({b => context:Blogs:Add(b)})
context:SaveChanges()
END METHOD
END CLASS
PUBLIC CLASS Blog
PUBLIC PROPERTY BlogId AS INT AUTO
PUBLIC PROPERTY Name AS STRING AUTO
PUBLIC VIRTUAL PROPERTY Posts AS List<Post> AUTO
END CLASS
PUBLIC CLASS Post
PUBLIC PROPERTY PostId AS INT AUTO
PUBLIC PROPERTY Title AS STRING AUTO
PUBLIC PROPERTY Content AS STRING AUTO
PUBLIC PROPERTY BlogId AS INT AUTO
PUBLIC PROPERTY Blog AS Blog AUTO
END CLASS
PUBLIC CLASS BloggingContext INHERIT DbContext
PUBLIC PROPERTY Blogs AS DbSet<Blog> AUTO
PUBLIC PROPERTY Posts AS DbSet<Post> AUTO
CONSTRUCTOR()
SUPER("DataContext")
SELF:Database:Log := Console.WriteLine
END CLASS
FUNCTION Start() AS VOID STRICT
Database.SetInitializer<BloggingContext>(CustomIntializer{})
BEGIN USING VAR db := BloggingContext{}
db:database:Initialize(force := TRUE)
db:Blogs.Add(Blog{} {Name := "Blog 1"})
db:SaveChanges()
END USING
Console.WriteLine("*** Database updated ***")
Console.ReadLine()
还有
app.config
:
<configuration>
<configSections>
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</configSections>
<entityFramework>
<defaultConnectionFactory
type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SQLite"
type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
</providers>
</entityFramework>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite.EF6" />
<add name="SQLite Data Provider (Entity Framework 6)"
invariant="System.Data.SQLite.EF6"
description=".NET Framework Data Provider for SQLite (Entity Framework 6)"
type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
<remove invariant="System.Data.SQLite" />
<add name="SQLite Data Provider"
invariant="System.Data.SQLite"
description=".NET Framework Data Provider for SQLite"
type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>
<connectionStrings>
<add name="DataContext"
connectionString="Data Source=pemo.db3;Version=3"
providerName="System.Data.SQLite.EF6" />
</connectionStrings>
</configuration>
我错过了什么,或者 SQLite 的代码优先不适用于 EF 6?
解决方案是直接通过重写的 OnModelCreating() 方法中的 SQL 语句创建表,如下所示:
PROTECTED OVERRIDE METHOD OnModelCreating(modelBuilder As DbModelBuilder) As Void
SELF:CreateTables()
SELF:OnModelCreating(modelBuilder.Entity<TitleInfo>())
SELF:OnModelCreating(modelBuilder.Entity<ArtistInfo>())
SUPER.OnModelCreating(modelBuilder)
RETURN
CreateTables() 方法完成所有工作:
/// <summary>
/// Create the tables of the database
/// </summary>
METHOD CreateTables() AS VOID
VAR sqlText := "CREATE TABLE IF NOT EXISTS Titles (Id INTEGER PRIMARY KEY AUTOINCREMENT, Title TEXT NOT NULL, "
sqlText += "ArtistID TEXT, Year INTEGER, Album TEXT, Length REAL, Rating INTEGER, Disco INTEGER,"
sqlTExt += "AlbumCover BLOB, Remark TEXT);"
VAR connectionString := Database:Connection:ConnectionString
BEGIN USING VAR dbConnection := SQLiteConnection{connectionString}
dbConnection:Open()
BEGIN USING VAR dbCommand := dbConnection:CreateCommand()
dbCommand:CommandText := sqlText
dbCommand:ExecuteNonQuery()
END USING
sqlText := "CREATE TABLE IF NOT EXISTS Artists (Id TEXT PRIMARY KEY NOT NULL, Name TEXT NOT NULL);"
BEGIN USING VAR dbCommand := dbConnection:CreateCommand()
dbCommand:CommandText := sqlText
dbCommand:ExecuteNonQuery()
END USING
END USING
END METHOD
还有一点很重要,不要通过调用 SetInitializer() 方法来创建数据库文件:
// Don't create the database automatically
METHOD SetInitializeNoCreate() AS VOID
Database:SetInitializer<MusicDbContext>(NULL)
END METHOD
并且基类构造函数不应使用命名参数但不带命名参数来调用:
Constructor()
// Call the base class constructor with the connection string name
// Don't use the name= for SQLite
Super("80MusicCon")
这样就创建了连接字符串中所述的 db3 数据库文件:
<connectionStrings>
<add
name="80MusicCon"
connectionString="Data Source=./output/80musics.db3"
providerName="System.Data.SQLite"
/>
</connectionStrings>
无需迁移。
虽然推荐的方法当然是使用Nuget包,但输出目录中所需的程序集是: