第二章(2) 从已有数据库中创建模型
问题:
你的数据库中已经有了几张表,也许还有几张视图、一些外键约束的设置,所以现在你想根据现有的数据库创建模型。
解决过程:
我们假设你的数据库是用来描述诗人和他们的诗集的,关系图如图2-2-1所示:
图2-2-1.
从上面这张图中,我们可以看到,一个诗人可以是很多诗篇的作者,每一首诗会根据它的格律(Meter)分类,
要把视图、表和关系这些引入模型,需要做如下几项事情:
- 在你的项目上右键,选择“新建像项”。
- 从Visual C# Item 的Data Templates中选择ADO.NET Entity Data Model。
- 选择从已有数据库中生成模型,点击下一步。
- 你可以选择新建数据库连接,也可以选择已有的连接。如果你要新建连接的话,你需要选择你的数据库服务器、你授权的方式(Windows或者SQL Server),然后再选择数据库。这些都做好了之后,最好点击“测试连接”来保证连接准备就绪。测试完成后,点击下一步。
5.下一步出现的对话框显示了你选择的数据库中所有的表、视图和存储过程(PS:EF貌似现在也支持这三个)。选择你要加入模型中的项,这里我们选择表Meter、Poem和Poet,视图vwLibrary。选中最下面的两个复选框,待会我们会再讲到。如图2-2-2所示:
图2-2-2
6.当你点击完成之后,向导会帮你创建一个新的模型。模型里面有我们的三张表和一个视图,当然也会把数据库中的外键约束加入进去,然后推断出Poet和Poem(s)是一对多的关系,Meter和Poem(s)也是一对多的关系。图2-2-3显示了新的模型中各个表和视图的关系:
图2-2-3
现在模型有了,你就可以在你的代码中用它了。要注意的是vwLibrary这个实体是基于数据库中的视图。在很多数据库中,视图都是只读的,插入、删除和更新这些操作不会对数据库层造成影响。在EF中也是如此,EF认为视图是只读的,在第六章我们会细讲。
原理:
让我们一起来看下刚才创建起来的这个视图。首先注意到的是这些实体都有标量属性和导航属性。标量属性是映射到实际数据库表中每一列的,而导航属性则是根据表和表之间的关系派生出来的。在图中,一首诗有一个格律和一个作者,这对应了Meter和Poet两个导航属性。如果你有一个Poem实例,那么这个Poem实例中的Poet导航属性会被赋上一个Poet实例的值,Meter亦然。 对于Poet(诗人)实体,他会写很多诗,那么他的Poems属性可能是一系列Poem实例的集合。当然这个集合可能为空,也可能是若干。Meter实体的Peom导航属性也是同样的。但是,我们的数据库中并没有包含任何关于视图vwLibrary的关系,所以图2-2-3的模型中vwLibrary的导航属性是为空的。
然后我们注意到的是导航属性中的名字用了复数形式(一对多的“多”方有复数,“一”方仍为单数)。如果你在实体上右键查看它们的属性,你会发现每个实体集的名字都是复数的。例如,Poem实体的名字是Poems。这个名字复数化是自动地生成的,因为我们前面把Pluralize or singularize generated object names的选项勾上了。
而Include Foreign Key Columns in the model 这个选项则是将外键关系也加入了模型中。尽管把外键关系和导航属性都加入进来,看起来似乎有些没必要,在后面的很多recipes中我们发现直接通过外键关系来访问也很有用。
下面的例子演示了怎么用在模型中Poet、Poem和Meter实例,怎么将这些实例保存到数据库中。代码中也写出了应该怎么样从数据库中查询模型。
第一段代码,我们创建了Poet、Poem和Meter实体类型的实例,也就是诗人约翰 米尔顿,他的诗篇“失乐园”,格律是“抑扬格五音”。当所有值都赋好了值后,调用SaveChanges()方法生成并执行SQL语句,来在底层数据库中插入行,
using (var context = new EFRecipesEntities()) { var poet = new Poet { FirstName = "John", LastName = "Milton" }; var poem = new Poem { Title = "Paradise Lost" }; var meter = new Meter { MeterName = "Iambic Pentameter" }; poem.Meter = meter; poem.Poet = poet; context.Poems.AddObject(poem); poem = new Poem { Title = "Paradise Regained" }; poem.Meter = meter; poem.Poet = poet; context.Poems.AddObject(poem); poet = new Poet { FirstName = "Lewis", LastName = "Carroll" }; poem = new Poem { Title = "The Hunting of the Shark" }; meter = new Meter { MeterName = "Anapestic Tetrameter" }; poem.Meter = meter; poem.Poet = poet; context.Poems.AddObject(poem); poet = new Poet { FirstName = "Lord", LastName = "Byron" }; poem = new Poem { Title = "Don Juan" }; poem.Meter = meter; poem.Poet = poet; context.Poems.AddObject(poem); context.SaveChanges(); }
using (var context = new EFRecipesEntities()) { var poets = from p in context.Poets select p; foreach (var poet in poets) { Console.WriteLine("{0} {1}", poet.FirstName, poet.LastName); foreach (var poem in poet.Poems) { Console.WriteLine("\t{0} ({1})", poem.Title, poem.Meter.MeterName); } } } // using our vwLibrary view using (var context = new EFRecipesEntities()) { var items = from i in context.vwLibraries select i; foreach (var item in items) { Console.WriteLine("{0} {1}", item.FirstName, item.LastName); Console.WriteLine("\t{0} ({1})", item.Title, item.MeterName); } }
------------------------------------------------
Lord Byron
Don Juan (Anapestic Tetrameter)
Lewis Carroll
The Hunting of the Shark (Anapestic Tetrameter)
John Milton
Paradise Regained (Iambic Pentameter)
Paradise Lost (Iambic Pentameter)
Lewis Carroll
The Hunting of the Shark (Anapestic Tetrameter)
Lord Byron
Don Juan (Anapestic Tetrameter)
John Milton
Paradise Regained (Iambic Pentameter)
John Milton
Paradise Lost (Iambic Pentameter)
------------------------------------------------