zoukankan      html  css  js  c++  java
  • 【转】【翻译】Code Only增强

    【原文地址】Code Only Enhancements
    【原文发表日期】 03 August 09 11:11

    自从第一个预览版发布之后,我们一直在奋力增强Code Only功能。

    在下一个版本中,你将能够指定
    1. 导航属性的倒转(Inverse)关系
    2. 属性的细节(Facets),象like Nullability(可null性), MaxLength(最大长度), Precision(精度)等等
    3. 属性与字段间的映射
    4. 类型与数据表间的映射
    5. 继承策略
    6. 配置的封装

    本贴的余下部分将依次对这些特性进行详述。

    注册导航属性的倒转关系:

    你现在可以注册倒转关系,即一个导航属性到另一个导航属性的倒转(inverse)关系,象这样:

    builder.RegisterInverse(
           (Customer c) => c.Orders,
           (Order o) => o.Customer)
    );

    这代码表示Customer.Orders是Order.Customer关系的另一头。把order1 加到customer1.Orders集合中去,与把order1.Customer设置成customer1具有同等的效果。

    指定属性细节(Facets):

    你还可以指定属性的细节,即象Nullability(可null性), MaxLength(最大长度), Precision(精度)等等这样的东西,象这样:

    var customerConfig = new EntityConfiguration<Customer>();
    // 我们可以推断出ID是主键
    // 但无法推断出它在插入时是在数据库生成的
    customerConfig.ForProperty(c => c.ID)
                  .Identity();
    customerConfig.ForProperty(c => c.Name)
                  .MaxLength(100)
                  .NonUnicode();
    customerConfig.ForProperty(c => c.Website)
                  .MaxLength(200)
                  .Nullable()

    builder.Configure(customerConfig);

    这把Customer类型配置成:

    • ID 属性是Identity字段,即在我们插入数据到数据库时,其值是由数据库计算出来的。
    • Name属性,其MaxLength为100个字符,是NonUnicode(非Unicode),即在SQL Server中是VARCHAR而不是NVARCHAR。
    • Website属性,其MaxLength为200个字符串,是Nullable。

    这些细节是针对概念模型Conceptual Model,即CSDL)的,从那里,也传到数据库(即SSDL)。

    封装细节配置

    你可以创建一个EntityConfiguration<T>的继承类来封装所有这些配置。

    例如:

    public class CustomerConfig: EntityConfiguration<Customer>
    {
        public CustomerConfig(){
           ForProperty(c => c.ID)
                     .Identity();
           ForProperty(c => c.Name)
                     .MaxLength(100)
                     .NonUnicode();
           ForProperty(c => c.Website)
                     .MaxLenght(200)
                     .Nullable();
        }
    }

    我们建议你创建象这样的类,而不是配置EntityConfiguration<>,因为封装的好处。

    指定数据表名

    在你使用Configure<T>(..) 时,实体框架为你推断出默认的映射,继承策略(TPH)和表名。

    但如果你要指定表名,你可以这么做:

    var customerConfig = new EntityConfiguration<Customer>();
    // 象上面那样配置细节
    ...

    // 用一个特定的表名注册配置
    builder.Tables[“dbo.Custs”] = customerConfig;

    指定映射:

    如果你需要对映射更多的控制(例如,要映射到一个现有的数据库或者使用企业的命名规则),那么你可以象这样指定映射:

    EntityMap<Customer> customerMap = 
        Map.OfType<Customer>(
            c => new {
                cid = c.ID,
                c.Name,
                csite = c.Website
            }
        );

    映射解释

    这个映射表明,ID是映射到‘cid’字段,Name属性是保存到‘Name’字段,而Website是映射到‘csite’ 字段的。

    没有被引用的属性是不会被持久化的,就象使用实体框架默认的代码生成时生成的部分类上的属性一样。

    LINQ内涵句法(Comprehension Syntax)

    你甚至也可以使用LINQ内涵句法来指定同样的事情:

    EntityMap<Customer> customerMap =
        from c in Map.OfType<Customer>()       
        select new {
                cid = c.ID,
                c.Name,
                csite = c.Website
            };

    用映射指定细节(Facets)

    在配置完映射后,你还可以象这样在映射上指定facets:

    customerMap.ForProperty(c => c.ID)
               .Identity();
    customerMap.ForProperty(c => c.Name)
               .MaxLength(100)
               .NonUnicode();
    customerMap.ForProperty(c => c.Website)
               .MaxLenght(200)
               .Nullable();

    指定数据表

    最后一步是指派数据表映射。

    builder.Tables[“dbo.Custs”] = customerMap;

    至此,我们为Customer类指定了自定义表,映射和自定义facets。

    指定继承:

    CodeOnly默认所用的继承策略是Table Per Hierarchy (每个类分层结构共用一个数据表)(或 TPH)。

    但如果你需要一个不同的策略,你需要动手配置相应映射

    设想一下,如果你要映射三个类:Vehicle , Car 和 Boat,其中Car 和 Boat是从Vehicle继承而来,而Vehicle本身是个抽象类。

    clip_image001

    Table Per Hierarchy (TPH,每个类分层结构共用一个数据表)

    如果你想要使用TPH做映射,你可以这么做:

    var vehicleMap = 
        Map.OfTypeOnly<Vehicle>(
            v => new {
                vid = v.ID,
                v.Name,
                vdesc = v.Description
                v.MaxPassengers,
            }
        ).Union(Map.OfTypeOnly<Car>(
            c => new { 
                vid = c.ID, 
                c.Name,
                vdesc = c.Description 
                c.MaxPassengers,
                trans = c.Transmission,
                tspd = c.Topspeed,
                ccty = c.EngineCapacity,
                ncyld = c.NoCylinder,
                discriminator = “CAR”
            })
        ).Union(Map.OfTypeOnly<Boat>(
            b => new { 
                vid = b.ID, 
                b.Name,
                vdesc = b.Description 
                b.MaxPassengers,
                lng = b.Length,
                b.HasSail,
                b.HasEngine
                discriminator = “BOAT”
            })
        );

    builder.Tables[“dbo.vehicles”] = vehicleMap;

    在TPH映射中:

    1. OfTypeOnly() 用来创建映射片段。
    2. 然后,映射片段可以联合(union)起来,这样,它们就可以指派到一个表上(TPH中整个类分层结构共用一个数据表)。
    3. 每个非抽象类型需要一个鉴别器字段(discriminator column),该字段可以是任何名称,但类分层结构中的每个非抽象类型必须有一个不同的常数(即“CAR”)。
    4. 在映射继承类时,你必须重新映射基类中已经映射了的所有属性。

    Table Per Type (TPT,每个类型一个表)

    如果你想要使用Table Per Type (每个类型一个表) 或TPT来映射同个类分层结构,你该这么做:

    builder.Table[“dbo.Vehicles”]= 
        Map.OfType<Vehicle>(
            v => new {
                vid = v.ID,
                v.Name,
                vdesc = v.Description
                v.MaxPassengers,
            }
        );

    builder.Tables[“dbo.Cars”] = 
        Map.OfType<Car>(
            c => new {
                cid = c.ID,
                trans = c.Transmission,
                tspd = c.Topspeed,
                ccty = c.EngineCapacity,
                ncyld = c.NoCylinders,
             }
         );

    builder.Tables[“dbo.Boats”] = 
        Map.OfType<Boat>(
            b => new {
                bid = b.ID,
                lng = b.Length,
                b.HasSail,
                b.HasEngine
            }
        );

    在TPT映射中:

    1. OfType<>()用于每个映射“片段”。
    2. 每个映射“片段”指派到不同的表上。
    3. 每个映射“片段”只映射在当前类上声明的属性,除非。。。
    4. 键属性必须在每个片段中映射(这允许参与该类型的表间的JOIN)。
    5. 没有鉴别器字段。

    Table Per Class (TPC,每个类一个表,【译注】常规的说法是,每个非抽象类或每个具体类一个表)

    你还可以象这样使用TPC来映射这个类分层结构:

    builder.Tables[“dbo.Cars”] = 
        Map.OfTypeOnly<Car>(
            c => new {
                cid = c.ID,
                c.Name,
                vdesc = c.Description
                c.MaxPassengers,
                trans = c.Transmission,
                tspd = c.Topspeed,
                ccty = c.EngineCapacity,
                ncyld = c.NoCylinders,
             }
         );

    builder.Tables[“dbo.Boats”] = 
        Map.OfTypeOnly<Boat>(
            b => new {
                bid = b.ID,
                b.Name,
                description = b.Description
                b.MaxPassengers,
                lng = b.Length,
                b.HasSail,
                b.HasEngine
            }
        );

    在TPC映射中:

    1. 跟TPH一样,我们使用OfTypeOnly(..)。
    2. 但跟TPH不一样,每个非抽象的类型拥有自己的表(所以,Vehicle没有自己的表,因为它是个抽象类)。
    3. 每个映射片段重新映射每个持久(non-transient)属性。
    4. 类分层结构中的抽象类没有映射。
    5. 没有鉴别器字段。

    默认的外键的位置:

    如果我们看到一个引用(譬如Order.Customer),我们假定其多重性(multiplicity)为0..1。意即,其外键或FK是可null的。

    如果我们看到一个集合(譬如Customer.Orders),我们假定其多重性为多(many)。

    然后,在注册倒转(inverse)时,我们知道一个关系的两头的多重性,譬如,在上面的例子中,我们知道,每个Order可以有0..1个 Customer,每个Customer可以有多个Order。

    所以按约定,共有三种主要的关系类型,我们需要推断出其FK的位置:

    0..1 to many –> 按约定,我们把FK放在many一端,所以上面的Customer.Orders例子中, FK放在Orders 表上。 

    many to many –> 没什么选择,只能引进一个连接表(join table)。

    0..1 to 0..1
    –> 你可以配置FK应该在什么地方,但不配置的话,我们会引进一个连接表(join table)。

    但有时候,引用可以不是0..1,而是1,例如,FK(不管它在什么地方),也许不可以null的。

    你可以这样来指定FK不是nullable的:

    var orderConfig = builder.Configure<Order>();
    orderConfig.RegisterInverse(o => o.Customer, c => c.Orders);
    orderConfig.ForProperty(o => o.Customer).NonNullable();

    这告诉我们,每个Order正好有一个1个Customer,而每个Customer有多个Orders。

    能够区分一个引用是非Nullable,会引进几个新的多重性组合,对此,我们也需要约定:

    1 to many –> 按约定,我们把FK放在many的一端, 并且在数据库中将其设置为非nullable。

    0..1 to 1 –> 按约定,我们把FK放在1的一端, 并且将其设置为非nullable。

    1 to 1 –> 跟0..1 to 0..1一样,我们无法决定该把FK放在何处,所以按约定,我们要引进一个连接表(join table)。

    指定外键(FK)映射:

    至此,我们为实体的属性创建了映射。

    那么导航属性和外键怎么办?

    所有的关系类型(除了many to many)可以不用数据库中的连接表(join table)来建模,所以我们允许你象这样,作为EntityMap的一部分来映射外键:

    EntityMap<Customer> customerMap =
        from c in Map.OfType<Customer>()       
        select new {
                cid = c.ID,
                c.Name,
                csite = c.Website,
                salesPersonFK = c.SalesPerson.ID
            };

    customerMap.RegisterInverse(c => c.SalesPerson, s => c.Clients);
    builder.Tables[“dbo.Custs”] = customerMap;

    这指定,Customer.SalesPerson导航属性,以及它的倒转(inverse)SalesPerson.Customers是保存在 dbo.Custs表的salesPersonFK字段中的。因为映射片段把 salesPersonFK 字段映射到了c.SalesPerson.ID上,而 SalesPerson.ID是相关的SalesPerson实体的主键(或者是主键的一部分),当然,外键是指向主键的。

    指定连接表(Join Table)映射:

    在many to many关系的情形下,你必须有一个连接表,所以按约定,没有映射信息,我们就会产生一个连接表(join table)。

    但假如你需要更多的控制,你可以这么做:

    var blogPostsMap = new AssociationMap<Blog, Post>(
         b => b.Posts
    ).Map(
        (b, p) => new {BlogId = b.ID, PostId = p.ID}
    );

    builder.Tables[“dbo.BlogPosts”] = blogPostsMap;

    这是说, Blog 与 Post间的many to many关系是保存在dbo.BlogPosts表中的,它有2个字段:

    • ‘BlogId’ 是个外键,指向Blog的ID属性映射到的字段。
    • ‘PostId’ 是个外键,指向Post的ID属性映射到的字段。

    分割实体(Entity Splitting):

    Code Only甚至还支持象分割实体这样高级的映射策略:

    builder.Tables[“dbo.Customer”] = Map.OfType<Customer>(
       c => new {
          cid = c.ID,
          c.Name,
          active = c.IsActive
       }
    );

    builder.Tables[“dbo.CustomerDetails”] = Map.OfType<Customer>(
        c => new {
           cid = c.ID,
           c.Size,
           c.Industry
        }
    );

    这是说,Customer实体是分开保存在dbo.Customer和dbo.CustomerDetails两个表中的。

    封装所有的配置:

    你还可以编写一个类,从EntityMap<T>继承而来,含有所有的映射,facets等等。例如,下面是一个类,包含了Product的配置:

    public class ProductMap: EntityMap<Product>
    {
        public ProductMap{
             this.Map( p => new {
                  pid = p.ID,
                  pcode = p.Name,
                  cid = p.Category.ID
             });
             this.ForProperty(p => p.ID).Identity();
             this.ForProperty(p => p.Name).MaxLength(100)
                 .NonUnicode();
             this.ForProperty(p => p.Category).NonNullable();
             this.RegisterInverse(p => p.Category,
                                  c => c.Products);   
         }
    }

    这是个高度推荐的做法,因为配置Product类型变得容易之极:

    builder.Tables[“dbo.Products”] = new ProductMap();

    结束语:

    正如你看到的,我们正在计划许多的增强,以允许最核心的场景。

    你觉得怎么样?你喜欢这些API么?有什么东西你想要改变的?

    一如既往,我们期待听到你的反馈。

    Alex James
    微软Entity Framework开发团队的Program Manager

    本贴是Entity Framework开发团队的透明设计实践的一部分。想了解其工作原理,以及你的反馈是如何被使用的,请参阅 这个贴子



    推荐文章
  • 相关阅读:
    阅读之分布式架构的数据一致
    阅读之MySQL数据库分表
    阅读笔记1
    问题账户需求分析
    软件需求分析阅读笔记
    开发进度第四天
    开发进度第三天
    开发进度第二天
    线程中三个关键对象闭锁,栅栏,信号量
    java多线程中关于原子操作
  • 原文地址:https://www.cnblogs.com/fcsh820/p/1866356.html
Copyright © 2011-2022 走看看