EFCore2.0@Xamarin.Forms
?? ? 由于忙于Xamarin的書的創作很久沒有和大家見面了,回到博客我會陸續更新一些最新的Xamarin技術,還有最近一直在努力的人工智能相關知識。話說csdn的博客改版了。總覺得變化是好事情啊。
? ? ? 這篇博客,我想和大家說說EFCore,在.NET社區,EntityFramework對持久化有很深遠的影響,特別是O/RM節省了一堆的數據庫訪問代碼。到了.NET Core的年代,EF也升級到EntityFrameworkCore. 微軟視EFCore為一個跨平臺,輕量級,可擴展的數據庫操作技術。當然我更喜歡用Dapper, 但EFCore對于移動化的數據操作場景是非常合適的。這也是我今天的重點。
? ? ??
? ? ? 這里得提提移動端數據持久化,在移動端有很多方式,如CoreData, SQLite,還有第三方的大名鼎鼎的Realm(雖然很cool,但我總覺得有點復雜,因人而異了)。在Xamarin中用EFCore是有先天優勢的,特別是在支持.NET Standard項目中。如果你的項目采用SQLite作為你數據持久化的一部分,EFCore會是你的最佳搭檔。如果你希望的是其他持久化方案,那個人還是說一句不太合適。
? ? ?如果你想為原有的項目添加EFCore方案,你需要把你的PCL遷移到.NET Standard上,如果是新項目就直接選用.NET Standard上算了。這里吐槽下Visual Studio團隊,Windows下已經有Xamarin的.NET Standard模版(左圖),但macOS下是沒有的(右圖)。(Build快到了,你就能不能給我一個統一的呢?還有別告訴我在Beta,大家需要的是Stable)如果你希望在macOS下創建.NET Standard的Xamarin項目,建議你去裝一個Prism模版,或者在Windows下創建好扔過去,還有自己創建.NET Standard Library替換PCL。
? ? ??? ??
? ? ?創建好后,在.NET Standard下添加Nuget庫Microsoft.EntityFrameworkCore.Sqlite。添加成功后剩下的事就是做Model和DbContext.
CourseInfo.cs
? ? public class CourseInfo
? ? {
? ? ? ? [Key]
? ? ? ? public int courseID { get; set; }
? ? ? ? public string courseName { get; set; }
? ? ? ? public List<CourseSession> sessionList { get; set; }
? ? }
CourseSession.cs
? ? public class CourseSession
? ? {
? ? ? ? [Key]
? ? ? ? public int sessionID { get; set; }
? ? ? ? public string sessionName { get; set; }
? ? ? ? public int courseID { get; set; }
? ? ? ? public CourseInfo course { get; set; }
? ? }
CourseDbContext.cs
? ? public class CourseDbContext : DbContext
? ? {
? ? ? ? public DbSet<CourseInfo> Courses { get; set; }
? ? ? ? public DbSet<CourseSession> Sessions { get; set; }
? ? ? ? private string DatabasePath { get; set; }
? ? ? ? public CourseDbContext()
? ? ? ? {
? ? ? ? }
? ? ? ? public CourseDbContext(string databasePath)
? ? ? ? {
? ? ? ? ? ? DatabasePath = databasePath;
? ? ? ? }
? ? ? ? protected override void OnModelCreating(ModelBuilder modelBuilder)
? ? ? ? {
? ? ? ? ? ? modelBuilder.Entity<CourseSession>()
? ? ? ? ? ? ? ? ? ? ? ? .HasOne(p => p.course)
? ? ? ? ? ? ? ? ? ? ? ? .WithMany(b => b.sessionList)
? ? ? ? ? ? ? ? ? ? ? ? .HasForeignKey("courseID");
? ? ? ? }
? ? ? ? protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
? ? ? ? {
? ? ? ? ? ? optionsBuilder.UseSqlite($"Filename={DatabasePath}");
? ? ? ? }
? ? }
?? ? 這里讓我解釋一下,上面很簡單就是創建兩個表一個是課程CourseInfo,一個是課程內容CourseSession,我做了一個1:n的關聯,數據源就是CourseDbContext.?
? ? ? 在MainPage.cs要做的事就是把數據庫初始化好,和初始化一些數據。
? ? ? ? ? ? ? ?try
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? using (var db = new CourseDbContext(path))
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? //db.Database.OpenConnection();
? ? ? ? ? ? ? ? ? ? ? ? await db.Database.MigrateAsync();
? ? ? ? ? ? ? ? ? ? ? ? CourseInfo info1 = new CourseInfo() { courseID = 1000, courseName = "Xamarin in action" };
? ? ? ? ? ? ? ? ? ? ? ? CourseInfo info2 = new CourseInfo() { courseID = 1001, courseName = "Azure" };
? ? ? ? ? ? ? ? ? ? ? ? List<CourseInfo> courseList = new List<CourseInfo>() { info1, info2 };
? ? ? ? ? ? ? ? ? ? ? ? if (await db.Courses.CountAsync() < 2)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.Courses.AddRangeAsync(courseList);
? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.SaveChangesAsync();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? CourseSession session1 = new CourseSession() { sessionID = 1, sessionName = "1. What's Xamarin", courseID = 1000 };
? ? ? ? ? ? ? ? ? ? ? ? CourseSession session2 = new CourseSession() { sessionID = 2, sessionName = "1. What's azure", courseID = 1001 };
? ? ? ? ? ? ? ? ? ? ? ? CourseSession session3 = new CourseSession() { sessionID = 3, sessionName = "2. Xamarin Installation", courseID = 1000 };
? ? ? ? ? ? ? ? ? ? ? ? List<CourseSession> sessionList = new List<CourseSession>() { session1, session2,session3 };
? ? ? ? ? ? ? ? ? ? ? ? if (await db.Sessions.CountAsync() < 3)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.Sessions.AddRangeAsync(sessionList);
? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.SaveChangesAsync();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? var list = await db.Sessions.ToListAsync();
? ? ? ? ? ? ? ? ? ? var session = list.Where(item => item.courseID == 1000);
? ? ? ? ? ? ? ? ? ? ObservableCollection<SessionView> viewList = new ObservableCollection<SessionView>();
? ? ? ? ? ? ? ? ? ? foreach (var item in session)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? viewList.Add(new SessionView { SessionName = item.sessionName });
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? SessionListView.ItemsSource = viewList;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? catch (Exception ex)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? var temp = ex.ToString();
? ? ? ? ? ? ? ? }
?? ? 這樣通過EFCore的代碼就完成了,但別忘記在iOS/Android/UWP對SQLite進行初始化,否則你是不能使用SQLite的。方法如下,在每個平臺上添加SQLitePCL,
? ? ? ?iOS在AppDelegate.cs的FinishedLauching方法上添加
?SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
? ?Android在MainActivity.cs上增加
? ? ? ? [DllImport("libsqlite.so")]
? ? ? ? internal static extern int sqlite3_shutdown();
? ? ? ? [DllImport("libsqlite.so")]
? ? ? ? internal static extern int sqlite3_initialize();
? ? ??
?并在OnCreate方法上添加
? ? ? ? sqlite3_shutdown();
? ? ? ? SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());
? ? ? ? sqlite3_initialize();
?UWP呢?哈哈在Surface Phone出生前,我不會再去考慮了(希望真的有)
? ? ? ?這個時候應該可以編譯了吧?如果直接運行會出現以下錯誤:
? ? ? ?Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1:
? ? ? ?原因是項目缺少了Migrations對數據庫結構進行初始化。你需要做一個Migration, 個人建議你創建一個.NET Core Console Application去完成。這樣省事,又方便。創建好一個.NET Core Console Application后先添加Microsoft.EntityFrameworkCore.Design和Microsoft.EntityFrameworkCore.Sqlite, 之后去修改.csproj, 增加ef命令支持
? <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.2" />
? ? ? ?保存好后把之前創建的CourseInfo,CourseSession,CourseDbContext添加進來,restore和rebuild后,去命令行做?
?如果成功就可以生成一個Migrations文件夾,把這個文件夾拷貝進Xamarin.Forms .NET Standard的項目中。再重新運行就可以成功了。
? ? ? ?? ??
? ? ? EFCore + Xamarin.Forms就大功告成了,但我這里得提一個坑,在編譯Android項目時,不知道為啥就是不能部署到機器里面,我找了又找,需要做在Android的csproj上設置以下,添加如下:
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.3.0" />
<PropertyGroup>
? <NoWarn>$(NoWarn);NU1605</NoWarn>
</PropertyGroup>
應該是Xamarin.Android對.NET Standard兼容未做得太好,但通過這個方法可以避免相關錯誤。當然這個不影響部署發布了,錯誤我已經反饋給微軟那邊了,希望在新的版本中盡快修正。
? ? ? 完整代碼https://github.com/lokinfey/EFCore-Xamarin.Forms
? ? ? EFCore是.NET Core下非常重要的一部分,對于移動端數據持久化也有非常重要的意義,我更寄望有更多的功能可以實現,或者如第三方的Realm能盡快對EFCore進行支持。
原文:https://blog.csdn.net/kinfey/article/details/80147341
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的EFCore2.0@Xamarin.Forms的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EF Core 2.0使用MsSql/M
- 下一篇: 发现 ASP.NET Core Sign