# 数据库

数据库是永久性的,结构化的数据仓库,有很多不同种类的数据库,但存储和查询业务数据的最常见类型是关系数据库,关系数据库使用SQL语言(Structured Query Language)来查询并操纵它们的数据。使用这样的数据库至少需要知道一些SQL知识,以便在编程语言中嵌入SQL语句,但是这可能会导致语言上的不方便,这就导致了Code First方法,使用这种方法不必使用SQL语言,因为它将C#中的对象和数据库中的表对应起来,操作C#中的对象就能操作数据库。

.Net 中支持Code First的类库是Entity Framework最新的版本,要介绍它首先要介绍两个概念:

  • 实体关系模型(Entity-relationship model),这个模型中包含了实体(Entity),属性(Attribute),和关系(Relationship)三个基本元素,其中实体和关系都可以有属性。在表示它们的图中,通常用矩形框代表实体,用菱形框来表示实体之间的关系,用椭圆或圆角矩形来表示实体的属性。
  • 对象关系映射(Object-relational mapping),是一种为了解决面向对象与关系数据库关联的问题,ORM主要由3个部分组成:域对象,关系数据,映射关系,通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中,读取数据库时将数据库中的数据转换成相应的对象。

那么什么是Entity Framework?它是微软官方提供的ORM工具,可以让开发人员将更多的时间放到业务逻辑层代码上,开发人员使用LINQ语言,操作对象类从而实现数据库操作,它有3中使用场景:

  1. 从数据库生成类对象
  2. 由实体类生成数据库表结构
  3. 通过数据库可视化设计器设计数据库,同时生成实体类。

这三种不同的应用场景对应Entity Framework的三种不同模式:

  1. Database First
  2. Code First
  3. Model First

EFUsage

# EntityFramework

EntityFramework框架如下:

EntityFrameworkArchitecture

EDM(实体数据模型)包括三个部分:

  • 概念模型:包含模型类和它们之间的关系,独立于数据库表的设计。
  • 存储模型:是数据库设计模型,包括表,视图,存储过程等。
  • 映射:包含有关如何将概念模型映射到存储模型的信息。

其余部分:

  • LINQ to Entities:用于编写针对对象模型的查询语言,返回在概念模型中定义的实体。
  • Entitiy SQL:类似于LINQ to Entities的语言,但是要复杂许多。
  • Object Services(对象服务):数据库的访问入口,负责数据具体化,面向对象中的实体与数据库记录的相互转化。
  • Entity Client Data Provider:将LINQ to Entity和Entity SQL转换成数据库可以识别的SQL查询语句,使用ADO.net向数据库发送数据。
  • ADO.Net Data Provider:标准的Ado.net与数据库通信。

# EF版本

.Net 有两个版本的EF框架,一个是Entity Framework 6,另一个是Entity Framework Core,它们之间有啥区别呢?

EntityFramework是一项尝鲜和测试化的数据获取技术,它在2008年第一次发布,作为 .Net Framework 3.5 SP1 和 Visual Studio 2008 SP1的一部分,从4.1版开始迁移到EntityFramework NuGet package。EF6运行在 .Net Framework 4.x和 .Net Core 3.0上。

Entity Framework Core 是EF6的完全重写,在2016年第一次发布,EF Core 是运行在.Net Core上的跨平台产品。EF Core被设计成为与EF6开发体验一致的库,很多上层的API保持一致,所以EF Core对于那些使用EF6的开发者比较亲切。

在未来EF Core将会提供不会再EF6中实现的新特性,比如alternate keys,batch updates,mixed client/database evaluation in LINQ queries.但是因为它是一个新的代码库,它也缺乏EF6有的一些特性。

# 运行环境

EF5由EF API和.net framework 4.0/4.5组成,而EF6是独立的EntityFramework.dll,不依赖于 .net framework,可使用NuGet安装。

# DBContext

DbContext是EntityFramework很重要的部分,连接域模型与数据库的桥梁,是数据库通信的主要类。

DbContext

它的主要内容如下:

  • EntitySet::包含了映射到表的实体集合

  • Querying:将Linq-To-Entities转译为Sql并发送到数据库

  • Change Tracking:从数据库获取entities后保留并跟踪实体数据变化

  • Persisting Data:根据entity状态执行Insert、update、delete命令

  • Caching:DbContext的默认第一级缓存,在上下文中的生命周期中存储entity

  • Manage Relationship:DbContext在DbFirst模式中使用CSDL、MSL、SSDL管理对象关系,Code first中使用fluent api 管理关系

  • Object Materialization:DbContext将物理表转成entity实例对象

# 实例操作

# 连接Maria数据库

需要先下载MariaODBC (opens new window),注意需要安装一个版本的64位和32位,才能在Visual Studio中工具 | 连接到ODBC 中检索出用户的数据源。

# EF6实例代码

先安装依赖包:

EF6FirstExample

注意各个包的版本号一定要一致,因为MySql.Data最新版本是不匹配MySql.Data.Entity的。先来创建一个表,并往里面填充数据:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ComponentModel.DataAnnotations;
using MySql.Data;
using MySql.Data.Entity;
using MySql.Data.MySqlClient;
using System.Data.Common;
using System.Data.Entity;
using System.Collections;

namespace TestEF6._0
{
    class Program
    {
        public class Student
        {
            [Key] public string Name { get; set; }
            public int Age { get; set; }
            public int Score { get; set; }
            public string Area { get; set; }

            public override string ToString()
            {
                return $"Name:{Name},Age:{Age},Score:{Score},Area:{Area}";
            }
        }

        [DbConfigurationType(typeof(MySqlEFConfiguration))]
        public class StudentContext : DbContext
        {
            public DbSet<Student> Students { get; set; }
            public StudentContext(DbConnection connection) : base(connection, true)
            {
            }
        }

        static void PrintContent(IEnumerable target)
        {
            foreach (var one in target)
            {
                Console.WriteLine(one);
            }

        }
        public static StudentContext CreateDbContext()
        {
            MySqlConnectionStringBuilder connectString = new MySqlConnectionStringBuilder();
            connectString.Server = "TestDatabase";
            connectString.Port = 8888;
            connectString.Database = "EFData";
            connectString.UserID = "root";
            connectString.Password = "111111";
            return new StudentContext((new MySqlConnectionFactory()).CreateConnection(connectString.ToString()));
        }
        static void Main(string[] args)
        {
            using (var stuDB = CreateDbContext())
            {
                stuDB.Students.Add(new Student
                {
                    Name = "zhang",
                    Age = 12,
                    Score = 68,
                    Area = "tianjin",
                });
                stuDB.Students.Add(new Student
                {
                    Name = "li",
                    Age = 18,
                    Score = 78,
                    Area = "tianjin",
                });
                stuDB.Students.Add(new Student
                {
                    Name = "liu",
                    Age = 20,
                    Score = 89,
                    Area = "hebei",
                });
                stuDB.Students.Add(new Student
                {
                    Name = "yuan",
                    Age = 30,
                    Score = 89,
                    Area = "tianjin",
                });
                stuDB.Students.Add(new Student
                {
                    Name = "huo",
                    Age = 40,
                    Score = 100,
                    Area = "hebei",
                });
                stuDB.SaveChanges();
                var queryResult = from stu in stuDB.Students
                                  orderby stu.Score, stu.Age
                                  select stu;
                Console.WriteLine("ALL Students in the database:");
                PrintContent(queryResult);
                Console.ReadKey();
            }


        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

注意在Mysql数据库中是不能有TestDatabase数据库的,否则会提示没有表。

现在再添加三个表,Bookracks,Books和Authors,规定一个书架可以有多本书,一本书只能有一个作者。

因为上面添加了一个Students表,再运行下面这段代码时会提示没有相应表,这个可以删除TestDatabase数据库再运行。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ComponentModel.DataAnnotations;
using MySql.Data;
using MySql.Data.Entity;
using MySql.Data.MySqlClient;
using System.Data.Common;
using System.Data.Entity;
using System.Collections;
using System.Xml.Linq;

namespace TestEF6._0
{
    class Program
    {
        public class Bookrack
        {
            [Key] public string Name { get; set; }

            public virtual List<Book> Books { get; set; }

            public override string ToString()
            {
                return $"Name:{Name}";
            }
        }

        public class Book
        {
            [Key] public string BookName { get; set; }
            public string Category { get; set; }
            public virtual Author Author { get; set; }
        }

        public class Author
        {
            [Key] public string AuthorName { get; set; }
            public int Age { get; set; }
        }

        [DbConfigurationType(typeof(MySqlEFConfiguration))]
        public class MyDbContext : DbContext
        {
            public DbSet<Bookrack> Bookracks { get; set; }
            public DbSet<Book> Books { get; set; }
            public DbSet<Author> Authors { get; set; }

            public MyDbContext()
            {

            }
            public MyDbContext(DbConnection connection) : base(connection, true)
            {
            }
        }

        static void PrintContent(IEnumerable target)
        {
            foreach (var one in target)
            {
                Console.WriteLine(one);
            }

        }
        public static MyDbContext CreateDbContext()
        {
            MySqlConnectionStringBuilder connectString = new MySqlConnectionStringBuilder();
            connectString.Server = "TestDatabase";
            connectString.Port = 8888;
            connectString.Database = "EFData";
            connectString.UserID = "root";
            connectString.Password = "111111";
            return new MyDbContext((new MySqlConnectionFactory()).CreateConnection(connectString.ToString()));
        }
        static void Main(string[] args)
        {
            using (var dB = CreateDbContext())
            {
                Author author1 = new Author
                {
                    AuthorName = "zhang",
                    Age = 30,
                };
                Author author2 = new Author
                {
                    AuthorName = "li",
                    Age = 25,
                };
                Author author3 = new Author
                {
                    AuthorName = "wang",
                    Age = 27,
                };
                Book book1 = new Book
                {
                    BookName = "english",
                    Category = "language",
                    Author = author1,
                };
                Book book2 = new Book
                {
                    BookName = "math",
                    Category = "science",
                    Author = author2,
                };
                Book book3 = new Book
                {
                    BookName = "chinese",
                    Category = "language",
                    Author = author3,
                };
                Bookrack bookrack1 = new Bookrack
                {
                    Name = "Left",
                    Books = new List<Book>{
                        book1,
                        book2,
                    }
                };
                Bookrack bookrack2 = new Bookrack
                {
                    Name = "Right",
                    Books = new List<Book>{
                        book3,
                    }
                };
                dB.Bookracks.Add(bookrack1);
                dB.Bookracks.Add(bookrack2);
                dB.SaveChanges();
                Console.WriteLine("report insert data:");
                var query = from Bookrack in dB.Bookracks
                            select Bookrack;
                foreach(var oneBookrack in query)
                {
                    Console.WriteLine($"bookrack name:{oneBookrack.Name}");
                    foreach(var oneBook in oneBookrack.Books)
                    {
                        Console.WriteLine($"book name:{oneBook.BookName},book category:{oneBook.Category},author name:{oneBook.Author.AuthorName},author age:{oneBook.Author.Age}");
                    }
                }

                foreach (var oneBookrack in query)
                {
                    XElement bookinfo = new XElement("bookrack",
                        new XAttribute("name", oneBookrack.Name),
                        from book in oneBookrack.Books
                        select new XElement("book",
                        new XAttribute("bookname", book.BookName),
                        new XAttribute("bookcategory", book.Category),
                        new XElement("author",
                            new XAttribute("name", book.Author.AuthorName),
                            new XAttribute("age", book.Author.Age)
                        )
                        )
                    );
                    Console.WriteLine(bookinfo);
                }

                Console.ReadKey();
            }
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

# 数据迁移

要实现自动迁移,可以在NuGet包管理器 | 程序包管理器中输入:

Enable-Migrations -EnableAutomaticMigrations
1

如果要撤掉自动迁移:

  1. 删除项目目录下的Migrations文件夹。

  2. 删除数据库中的__MigrationHistory表。

  3. 在DbContext派生类的构造器中,输入来禁用初始化

    Database.SetInitializer<DatabaseContext>(null);
    
    1
  4. 运行程序。