UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?

Posted 云中客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?相关的知识,希望对你有一定的参考价值。

选择SQLite的理由

在做UWP开发的时候我们首选的本地数据库一般都是Sqlite,我以前也不知道为啥?后来仔细研究了一下也是有原因的:

1,微软做的UWP应用大部分也是用Sqlite。或者说是微软推荐使用Sqlite吧!

2,简单!就只有一个类库没有多余的参照什么的。不像其他数据库还得做复杂配置什么的麻烦!

3,不需要数据库服务,数据服务和客户都在同一个进程里面。如下图:

1_SQLite

4,作为存储系统它只支持一个用户一个数据实体。

5,跨平台跨结构,这个好!

 

Sqlite主要使用内容

如果想充分使用好Sqlite数据库,建议保存好下面两个链接

  • The database connection handle
  • The prepared statement object
    操作上面对象主要的6个接口
  • sqlite3_open()
  • sqlite3_prepare()
  • sqlite3_step()
  • sqlite3_column()
  • sqlite3_finalize()
  • sqlite3_close()

     

    SQLite数据持久层:Entity Framework Core

    随着Entity Framework开源在.Net开发中越来越受欢迎,包括WinFrom,Asp.net MVC开发等等。为了跨平台在6.0后就是Entity Framwork Core版本。开源地址:https://github.com/aspnet/EntityFramework

    使用Entity Framwork Core当然得有个驱动才能调用SQLite数据库,那就是SQLite的ADO.NET驱动。大概层次如下:

    2_SQLite

     

    ORM具体实现

    此处做一个显示课程列表信息的UWP做实例。源代码:https://github.com/NewBLife/UWP/tree/master/SqliteEFCoreDemo

    1,新建一个空的UWP项目叫SqliteEFCoreDemo。

    2,添加包引用。

    Install-Package EntityFramework.SQLite –Pre

    Install-Package EntityFramework.Commands –Pre

    image

    3,创建Student和Course的Entity。

    // ***********************************************************************
    // FileName:Course
    // Description:
    // Project:
    // Author:NewBLife
    // Created:2016/5/28 21:25:32
    // Copyright (c) 2016 NewBLife,All rights reserved.
    // ***********************************************************************
    
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace SqliteEFCoreDemo.Models
    {
        /// <summary>
        /// 设置数据库表名
        /// </summary>
        [Table(name: "Course")]
        public class Course
        {
            /// <summary>
            /// ID不为空设置为Required
            /// </summary>
            [Required]
            public string ID { get; set; }
            public string Name { get; set; }
        }
    }

     

    // ***********************************************************************
    // FileName:Student
    // Description:
    // Project:
    // Author:NewBLife
    // Created:2016/5/28 21:23:45
    // Copyright (c) 2016 NewBLife,All rights reserved.
    // ***********************************************************************
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace SqliteEFCoreDemo.Models
    {
        /// <summary>
        /// 设置数据库表名
        /// </summary>
        [Table(name: "Student")]
        public class Student
        {
            /// <summary>
            /// ID不为空设置为Required
            /// </summary>
            [Required]
            public string ID { get; set; }
    
            public string Name { get; set; }
    
            public List<Course> Courses { get; set; }
        }
    }

    4,创建数据库操作EfDbContext继承于Microsoft.Data.Entity.DbContext。

    // ***********************************************************************
    // FileName:EfDbContext
    // Description:
    // Project:
    // Author:NewBLife
    // Created:2016/5/28 21:32:23
    // Copyright (c) 2016 NewBLife,All rights reserved.
    // ***********************************************************************
    using Microsoft.Data.Entity;
    
    namespace SqliteEFCoreDemo.Models
    {
        public class EfDbContext : DbContext
        {
            /// <summary>
            /// 注意:如果Student的Model里面没有设置表名将使用Students作为表名
            /// </summary>
            public DbSet<Student> Students { get; set; }
            public DbSet<Course> Courses { get; set; }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                // 配置数据库名
                optionsBuilder.UseSqlite("Filename=School.db");
            }
        }
    }
    5,编译项目(非常重要,不然有可能后续步骤出错)。

    6,高级操作:创建差分文件Migration。通过查看差分文件可以判断生成的表结构或者数据是不是正确(这是Entity Framework进行CodeFirst开发的便捷之处)。

    在程序包管理器中执行:add-migration initDb。将自动创建下图红色部分内容(这就是Entity Framework强大之处)。

    image

     

     

    自动生成的表结构代码如下:

    using System;
    using Microsoft.Data.Entity;
    using Microsoft.Data.Entity.Infrastructure;
    using Microsoft.Data.Entity.Metadata;
    using Microsoft.Data.Entity.Migrations;
    using SqliteEFCoreDemo.Models;
    
    namespace SqliteEFCoreDemo.Migrations
    {
        [DbContext(typeof(EfDbContext))]
        partial class EfDbContextModelSnapshot : ModelSnapshot
        {
            protected override void BuildModel(ModelBuilder modelBuilder)
            {
                modelBuilder
                    .HasAnnotation("ProductVersion", "7.0.0-rc1-16348");
    
                modelBuilder.Entity("SqliteEFCoreDemo.Models.Course", b =>
                    {
                        b.Property<string>("ID");
    
                        b.Property<string>("Name");
    
                        b.Property<string>("StudentID");
    
                        b.HasKey("ID");
    
                        b.HasAnnotation("Relational:TableName", "Course");
                    });
    
                modelBuilder.Entity("SqliteEFCoreDemo.Models.Student", b =>
                    {
                        b.Property<string>("ID");
    
                        b.Property<string>("Name");
    
                        b.HasKey("ID");
    
                        b.HasAnnotation("Relational:TableName", "Student");
                    });
    
                modelBuilder.Entity("SqliteEFCoreDemo.Models.Course", b =>
                    {
                        b.HasOne("SqliteEFCoreDemo.Models.Student")
                            .WithMany()
                            .HasForeignKey("StudentID");
                    });
            }
        }
    }

    自动生成的数据更新代码如下:

    using System;
    using System.Collections.Generic;
    using Microsoft.Data.Entity.Migrations;
    
    namespace SqliteEFCoreDemo.Migrations
    {
        public partial class initDb : Migration
        {
            protected override void Up(MigrationBuilder migrationBuilder)
            {
                migrationBuilder.CreateTable(
                    name: "Student",
                    columns: table => new
                    {
                        ID = table.Column<string>(nullable: false),
                        Name = table.Column<string>(nullable: true)
                    },
                    constraints: table =>
                    {
                        table.PrimaryKey("PK_Student", x => x.ID);
                    });
                migrationBuilder.CreateTable(
                    name: "Course",
                    columns: table => new
                    {
                        ID = table.Column<string>(nullable: false),
                        Name = table.Column<string>(nullable: true),
                        StudentID = table.Column<string>(nullable: true)
                    },
                    constraints: table =>
                    {
                        table.PrimaryKey("PK_Course", x => x.ID);
                        table.ForeignKey(
                            name: "FK_Course_Student_StudentID",
                            column: x => x.StudentID,
                            principalTable: "Student",
                            principalColumn: "ID",
                            onDelete: ReferentialAction.Restrict);
                    });
            }
    
            protected override void Down(MigrationBuilder migrationBuilder)
            {
                migrationBuilder.DropTable("Course");
                migrationBuilder.DropTable("Student");
            }
        }
    }

    每次执行Add-Migration 名称 都会根据你的Model修改创建差分的Migration文件。

    7,重量级操作:在App.xaml.cs的构造方法中添加如下代码来生成真正的数据库文件。

    public App()
            {
                this.InitializeComponent();
                this.Suspending += OnSuspending;
    
                using (var db = new EfDbContext())
                {
                    // 将差分文件写入数据库文件
                    db.Database.Migrate();
                }
            }

    8,添加演示布局以及Model的绑定设置。

    <Page
        x:Class="SqliteEFCoreDemo.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:SqliteEFCoreDemo"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:data="using:SqliteEFCoreDemo.Models"
        mc:Ignorable="d">
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <RelativePanel HorizontalAlignment="Center" Margin="0,50,0,0">
                <Grid Name="Header" Width="300">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="50"/>
                        <RowDefinition Height="50"/>
                        <RowDefinition Height="50"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Name="lblCID" 
                               Grid.Column="0"
                               Grid.Row="0"
                               Text="课程ID:"/>
                    <TextBox Name="txbCId" 
                               Grid.Column="1"
                               Grid.Row="0"
                               Width="200"/>
                    <TextBlock Name="lblCName" 
                               Grid.Column="0"
                               Grid.Row="1"
                               Text="课程名称:"/>
                    <TextBox Name="txbCName" 
                             Width="200"
                            Grid.Column="1"
                            Grid.Row="1" />
                    <Button Name="btnAdd"
                            Grid.Column="1"
                            Grid.Row="2"
                            Width="100"
                            Click="btnAdd_Click"
                            Content="Add Course"/>
                </Grid>
                <Grid Name="List"
                      RelativePanel.Below="Header">
                    <ListView Name="lstCourse">
                        <ListView.ItemTemplate>                        
                            <DataTemplate x:DataType="data:Course">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="100"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <TextBlock Grid.Column="0" Text="{x:Bind ID,Mode=OneWay}"/>
                                    <TextBlock Grid.Column="1" Text="{x:Bind Name,Mode=OneWay}"/>
                                </Grid>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </Grid>
            </RelativePanel>
        </Grid>
    </Page>
    using System.Linq;
    using SqliteEFCoreDemo.Models;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    //“空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 上有介绍
    
    namespace SqliteEFCoreDemo
    {
        /// <summary>
        /// 可用于自身或导航至 Frame 内部的空白页。
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
                this.Loaded += MainPage_Loaded;
            }
    
            private void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                using (var db = new EfDbContext())
                {
                    lstCourse.ItemsSource = db.Courses.ToList();
                }
            }
    
            private void btnAdd_Click(object sender, RoutedEventArgs e)
            {
                using (var db = new EfDbContext())
                {
                    var newCourse = new Course
                    {
                        ID = txbCId.Text.Trim(),
                        Name = txbCName.Text.Trim()
                    };
    
                    txbCId.Text = string.Empty;
                    txbCName.Text = string.Empty;
    
                    db.Courses.Add(newCourse);
                    db.SaveChanges();
    
                    lstCourse.ItemsSource = db.Courses.ToList();
                }
            }
        }
    }

    9,启动应用,添加数据试试。

    image

     

    10,查看自动创建的数据库文件。

    Not Null设置,外键设置等等都自动设置好了!好强大有没有…

    image

     

    总结

    用EFCore做数据库持久层只需要面向对象模型做处理就可以,与数据模型的交互等交给Entity Framework Core处理。总的来说使用很方便,节省不少工作量。

以上是关于UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?的主要内容,如果未能解决你的问题,请参考以下文章

基于Prism.Windows的UWP开发备忘

UWP开发之Template10实践二:拍照功能你合理使用了吗?(TempState临时目录问题)

UWP开发之Mvvmlight实践四:{x:bind}和{Binding}区别详解

[转]Android ORM系列之GreenDao最佳实践

Entity Framework 学习系列 - 认识理解Entity Framework

转 ofbiz之entity实体写法与解析