自己来控制EntityFramework4.1 Code-First,逐步消除EF之怪异现象

最近的项目开始使用EF4.1,拜读各路大侠文章数遍,满以为可以轻车熟路,却屡遭悲惨啊,怪异现象接连...

1,虽然使用Code-First模式,就是因为它代码整洁清爽条理,但还是习惯先建立数据表,再POCO...  结果发现Entity实体类与数据表的映射是EF自己独特智能操控的,比如实体类名为Product,它会智能映射成Products的表,加了个"s",然而,Category的实体类却映射成了Categories, 它居然能识别单词的复数写法,很神奇,难道它内置词典?要不然它该映射成Categorys才合理嘛(虽然它不是个单词),你说EF神奇么,真神奇!

后来,同事给个方法,解决了这个神奇的功能,我要自控,有些关键地方不需要EF来控制我的想法,于是在分类名上面添加一个特性[Table("映射的表名")]即可。

    [Table("Product")]
    public class Product
    {
        public int ID { get; set; }
        [Required]
        [Display(Name = "产品名")]
        public string Name { get; set; }
        public int CategoryID { get; set; }
        [Required]
        [Display(Name = "产品价格")]
        public Decimal? Price { get; set; }

        public virtual Category Category { get; set; }
    }

    [Table("Category")]
    public class Category
    {
        public int CategoryID { get; set; }
        /// <summary>
        /// 分类名
        /// </summary>
        [Required]
        [Display(Name = "分类名")]
        public string CategoryName { get; set; }
        public Int16 IsDel { get; set; }
        public virtual ICollection<Product> Products { get; set; }
    }

我后来又发现一个终极解决办法,那就是在Model建立的时候,移除EF内部的约定,神奇的约定

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // base.OnModelCreating(modelBuilder);   
            // modelBuilder.Entity<Order>().ToTable("Order"); 和实体类名的Table特性设置相同

            // 移除EF的表名公约  
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
            // 还可以移除对MetaData表的查询验证
            modelBuilder.Conventions.Remove<IncludeMetadataConvention>();

        }

2,每次通过EF查询数据库,会先Select ... MetaData,甚至SELECT Count(*) FROM sys.databases WHERE [name]=数据库名,前者的无用查询移除,在上面的OnModelCreating中可以取消了,但后者至今我还没找到办法。可以理解的是:EF之code-first会先查找数据库,如果没有则建立命名空间.CustomContext的库名,所以多了后者的查询,即便我在DbContext的类初始化中指定db名、或者连接字符串名,也是同样结果。而实体类和数据表是映射的,EF将实体类做了Hash计算以比对数据表,当发现实体类与数据表不对应的时候,会更新数据表,故而增加了查询metaData的动作。 其实对于我们固定的开发需求,这些功能,不能被关闭实在消耗性能。

3,EF是默认开启延迟加载,延迟加载的关系表中,必须建立表关系,也就是SQL的FK键。延迟加载非常好用,但存在性能问题,因为延迟加载关联到几个表,就要执行几次数据表查询,而不是一次性查询。这也是从字义上理解的达到一致。否则就属于贪婪加载,即Context上下文里的Include("实体名"),可以实现一次性一条SQL语句查询,但我奇怪的是Include的string参数为啥不是表名,反而是实体名却用字符串来传入。

4,最痛苦的是EF下实体类或表字段外键,命名中不能有"_"?经过我测试也不尽然,虽然我发觉到了这是和关联表定义有关系,可是我仍非常不解,例如:

    [Table("Product")]
    public class Product
    {
        public int ID { get; set; }
        [Required]
        [Display(Name = "产品名")]
        public string Name { get; set; }
        public int CategoryID { get; set; }
        [Required]
        [Display(Name = "产品价格")]
        public Decimal? Price { get; set; }

        public virtual Category Category { get; set; }
    }

    [Table("Order")]
    public class Order
    {
        [Key]
        public int ID { get; set; }
        public int Product_ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public DateTime CreateTime { get; set; }
        public virtual Product Product { get; set; }
    }

这是一对关系表,显然设计的是一张订单中只有一个产品,订单中的Product_ID属性对应产品实体的ID属性,已经在数据库中做好了关系。 我要的结果是:当打开一条订单记录时,联表查询得到该产品ID的产品信息,所以做了延时加载public virtual Product Product { get; set; }

非常怪异的现象是,执行Order实体上下文ToList等操作时,提示Product_ID1列不存在,因为这个字段和属性的确不存在,为什么EF会拼出这个字段来呢? 之所以说不尽然是"_"的原因,因为即使我将属性和字段修改成ProductsID,EF却又报错找不到Product_ID的字段,EF要找的这2个字段本身都不存在。它为什么呢? 只有当这个外键字段为ProductID时才正常,这个非常诡异!!! 慢慢我发现,是public virtual Product Product { get; set; } 这个属性,生成了外键ID对应关系的字段Product_ID,也就是EF自己用了"_"符号,但当写成ProductID时,又没有生成。。。很糊涂。。。

5,继续实验中

总结,EF用起来比较轻松,但掌握住还是比较费劲的,虽说EF把一切都封装好了,但了解它的内幕才能给自己更多的信心,至少目前还不能掌握,还不能控制,总是被EF控制,这并不是什么好事,继续努力!如果你有这方面的知识,分享下吧 :)

补充第4条的“怪异现象”,直接见代码:

    [Table("Order")]
    public class Order
    {
        [Key]
        public int ID { get; set; }
        public int Product_ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public DateTime CreateTime { get; set; }

        [ForeignKey("Product_ID")]  //增加一个外键特性,并制定外键字段  
        public virtual Product Product { get; set; }
    }

Product_ID是外键字段,关联了Product属性,因为Product的主键是ID,EF自己拼装了一个外键字段为“Product_ID”,所以与实际的字段重复,重命名了“Product_ID1”;而当外键属性为其他时,EF就会自己拼装一个“Product_ID”,所以会找不到这个列;但是当属性为ProductID时,EF不会去拼装外键字段,这一定又是EF的内部约定。所幸的是,现在我可以给Product关联的导航属性一个外键特性就OK了。

posted @ 2011-07-06 00:05  Richwong  Views(5642)  Comments(19Edit  收藏  举报