第二章(5)自引用关系建模

问题:

  数据库中有一张自引用的表,你想建立一个自引用的实体关系模型。

解决过程:

   假设自引用的表结构如图2-5-1所示。

图2-5-1

  建立自引用模型步骤如下:

  1. 在你的项目上右键,添加一个新的模型。选择添加新项,然后选择ADO.NET Entity Data Model。
  2. 选择从数据库中生成,点击下一步。
  3. 使用创建向导,选择一个已有数据库连接或者新建一个。
  4. 从“选择你的数据库对象”对话框中,选择PictureCategory表。保持最下面的两个复选框为选中状态。点击完成。

    向导会帮你创建如图2-5-2所示的模型:

图2-5-2

   上面的模型中包含了两个导航属性,分别是PictureCategory1和PictureCategory2.

 

原因:

     数据库关系的特征有度、多重性和方向性。

     度是参与到关系中的实体类型个数。一元和两元的关系是最常见的,三元和N元则是理论意义多语实际意义。

     多重性是指关系中每个实体类型终端的个数。有1对多,1对1 这些。

    方向性是指单向还是双向。

    EF的实体数据模型支持一种特殊的数据库关系叫做关联(Assioation)。在关联类型的关系中,度是一元或者两元,多重性是0..1(0或1), 或者是*,方向性是双向的。

  在这个例子中,度是一元的(只有PictureCategory这个实体型),多重性是0..1(0或1)和*,方向性也是双向的。

   自引用类型通常是有父子关系,每个父亲都可以有很多孩子,但一个孩子只有一个父亲。因为父端是0..1(0或1),所以孩子可以没有父亲。这正代表了你的根节点,也就是只有孩子没有父亲的节点。

  下面的代码递归枚举了从根节点往下的所有picture categories

        static void RunExample()
        {
            using (var context = new EFRecipesEntities())
            {
                var louvre = new PictureCategory { Name = "Louvre" };
                var child = new PictureCategory { Name = "Egyptian Antiquites" };
                louvre.Subcategories.Add(child);
                child = new PictureCategory { Name = "Sculptures" };
                louvre.Subcategories.Add(child);
                child = new PictureCategory { Name = "Paintings" };
                louvre.Subcategories.Add(child);
                var paris = new PictureCategory { Name = "Paris" };
                paris.Subcategories.Add(louvre);
                var vacation = new PictureCategory { Name = "Summer Vacation" };
                vacation.Subcategories.Add(paris);
                context.PictureCategories.AddObject(paris);
                context.SaveChanges();
            }
            using (var context = new EFRecipesEntities())
            {
                PictureCategory root = (from c in context.PictureCategories
                                        where c.ParentCategory == null
                                        select c).FirstOrDefault();
                Print(root, 0);
            }
        }
        static void Print(PictureCategory cat, int level)
        {
            StringBuilder sb = new StringBuilder();
            Console.WriteLine("{0}{1}", sb.Append(' ', level).ToString(), cat.Name);
            foreach (PictureCategory child in cat.Subcategories)
            {
                Print(child, level + 1);
            }
        }

 

 

Output
-------------------------------------------
Summer Vacation
Paris
Louvre
Egyptian Antiquites
Sculptures
Paintings
-------------------------------------------

 

  现在来看下代码。首先,我们创建并实例化我们的实体类型。通过把创建的子PictureCategory都加到louvre类下,我们就把这个类图个连接起来了。接着把louvre类填到paris类中。最后再把paris类添加到summer vacation类中。这样就把层次结构从底到上建起来了。

  一旦我们调用SaveChanges()放大,就会把这些都插入到数据库。接着我们就可以进行查询操作来看看真正有哪些记录是插入成功的。

  在读取部分,我们从根实体开始。这是一个没有父节点的实例,也就是我们summer vacation。有个这个根节点,我们可以调用自己写的Print()方法。Print()方法有两个参数,一个是PictureCategory的实例,另一个是level,或者叫做层次结构中的深度。因为summer vacation 是根节点,所以它的深度是0,我们Print方法里的应该写成Print(root, 0)。
  在Print方法中,我们把category的名字写出来,并且在前面加了空格表示它的深度。Append()方法产生空格的个数来表示深度。在递归部分,我们通过递归调用Print()方法来遍历所有的子节点,同时确保每深入1层,level要加1.当所有的子节点都遍历完了,推出即可。结果如上面显示的一样。
     在6—5中,我们会介绍另外一种方法来解决这个问题,在存储过程中使用Common Table Expression来遍历整个类图,然后返回一个二维的结果集。

 

posted @ 2012-09-16 16:45  阿凡迪  阅读(1484)  评论(0编辑  收藏  举报