目錄
目前 EF 是 .NET 平臺下相當成熟的 ORM 框架,但是其最新發布的 6.x 版本依然不支援 SQLite 的 CodeFirst 模式,好在有大神已經在 Nuget 上釋出的相應的 Package 來解決這個問題。筆者通過做一個小實驗來驗證一下。
問題描述
SQLite 本身不支援 CodeFirst 模式,當我們的資料模型因業務變化而需要修改的話,那對應的資料庫表也要進行更改。這個時候,如果我們手動修改資料表的話就不太方便,因此我們需要想辦法讓其支援 CodeFirst 模式。筆者通過使用 SQLite.CodeFirst 來嘗試解決上述問題。
解決方案
安裝依賴包
首先我們建立一個控制檯程式,安裝如下 Package:
- System.Data.SQLite
- SQLite.CodeFirst
修改程式配置 App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<!--新增結點-->
<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>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<!--新增結點-->
<providers>
<provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
<provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
</providers>
</entityFramework>
<!--新增結點-->
<connectionStrings>
<add name="sampledb" connectionString="data source=.sampledb.db" providerName="System.Data.SQLite" />
</connectionStrings>
</configuration>
建立模型物件 Person.cs
[Table("person")]
public class Person
{
[Column("id"), Key, Autoincrement]
public int Id { get; set; }
[Column("firstname")]
public string FirstName { get; set; }
[Column("lastname")]
public string LastName { get; set; }
}
建立資料上下文 PersonDbContext.cs
public class PersonDbContext : DbContext
{
public DbSet<Person> People { get; set; }
/// <summary>
/// 從配置檔案讀取連結字串
/// </summary>
public PersonDbContext() :
base("name = sampledb")
{
ConfigurationFunc();
}
/// <summary>
/// 程式碼指定資料庫連線
/// </summary>
/// <param name="existingConnection"></param>
/// <param name="contextOwnsConnection"></param>
public PersonDbContext(DbConnection existingConnection, bool contextOwnsConnection) :
base(existingConnection, contextOwnsConnection)
{
ConfigurationFunc();
}
private void ConfigurationFunc()
{
Configuration.LazyLoadingEnabled = true;
Configuration.ProxyCreationEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var initializer = new SqliteDropCreateDatabaseWhenModelChanges<PersonDbContext>(modelBuilder);
Database.SetInitializer(initializer);
}
}
此時,當我們修改資料模型時,不需要執行任何 migration 操作就可以將 資料表對映到新的模型上。下面我們在主程式中呼叫一下:
主程式呼叫 Program.cs
class Program
{
static void Main(string[] args)
{
var ptah = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "sampledb.db");
var connection = SQLiteProviderFactory.Instance.CreateConnection();
connection.ConnectionString = $"Data Source={ptah}";
//using (var context = new PersonDbContext())
using (var context = new PersonDbContext(connection, false))
{
#region 預熱:針對資料表較多的情況下建議執行下述操作
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var mappingColection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
mappingColection.GenerateViews(new List<EdmSchemaError>());
#endregion
#region 插入資料
Console.WriteLine("插入資料:");
context.People.Add(new Person { FirstName = "hippie", LastName = "zhou" });
context.SaveChanges();
#endregion
#region 查詢資料
Console.WriteLine("查詢資料:");
foreach (var people in context.People)
{
Console.WriteLine($"{people.Id} - {people.FirstName} - {people.LastName}");
}
#endregion
#region 更改資料
Console.WriteLine("更改資料:");
Person person = context.People.Where(p => p.Id == 1)?.FirstOrDefault();
person.LastName = "Puth";
context.SaveChanges();
#endregion
#region 刪除資料
Console.WriteLine("刪除資料:");
Person person = context.People.Where(p => p.Id == 1)?.FirstOrDefault();
context.People.Remove(person);
context.SaveChanges();
#endregion
Console.ReadKey();
}
}
}
注意事項
由於 SQLite.CodeFirst 的 Package 需要依賴 EntityFramework
,以及為了能保證資料操作支援相關 LINQ 操作,在上述兩個 Pacakge 安裝的過程中,會同時安裝其它依賴包,所以最終的專案依賴包如下所示:
- EntityFramework
- SQLite.CodeFirst
- System.Data.SQLite
- System.Data.SQLite.Core
- System.Data.SQLite.EF6
- System.Data.SQLite.Linq
此外,需要注意一下 System.Data.SQLite 與 System.Data.SQLite.Core 的區別:
System.Data.SQLite:The official SQLite database engine for both x86 and x64 along with the ADO.NET provider. This package includes support for LINQ and Entity Framework 6.
System.Data.SQLite.Core:The official SQLite database engine for both x86 and x64 along with the ADO.NET provider.
所以,為了能讓 SQLite 支援 CodeFirst ,還是一件挺曲折的事情啊。