转自:http://www.cnblogs.com/richwong/archive/2011/07/06/2098759.html

最近的项目开始使用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了。

(转)自己来控制EntityFramework4.1 Code-First,逐步消除EF之怪异现象的更多相关文章

  1. 2.简单的Code First例子(EF Code-First系列)

    现在假想,我们想要为讴歌学校创建一个应用程序,这个程序需要能够来添加或者更新学生,分数,教师还有课程信息. 代替之前我们的做法:先是创建数据库,现在我们不这么做,我们先来创建领域类,首先我来创建两个简 ...

  2. 【繁星Code】如何在EF将实体注释写入数据库中

    最近在项目中需要把各个字段的释义写到数据库中,该项目已经上线很长时间了,数据库中的字段没有上千也有上百个,要是一个项目一个项目打开然后再去找对应字段查看什么意思,估计要到明年过年了.由于项目中使用En ...

  3. Code Review 程序员的寄望与哀伤

    一个程序员,他写完了代码,在测试环境通过了测试,然后他把它发布到了线上生产环境,但很快就发现在生产环境上出了问题,有潜在的 bug. 事后分析,是生产环境的一些微妙差异,使得这种 bug 场景在线下测 ...

  4. Code First :使用Entity. Framework编程(8) ----转发 收藏

    第8章 Code First将走向哪里? So far, this book has covered all of the Code First components that reached the ...

  5. Code First :使用Entity. Framework编程(6) ----转发 收藏

    Chapter6 Controlling Database Location,Creation Process, and Seed Data 第6章 控制数据库位置,创建过程和种子数据 In prev ...

  6. EntityFramework Reverse POCO Code First 生成器

    功能强大的(免费)实体框架工具 Julie Lerman 实体框架是开源的,因此开发社区可以在 entityframework.codeplex.com 上共享代码. 但是不要将自己局限在那里寻找工具 ...

  7. NK3C 业务权限控制

    资源中,添加了一个类型:权限(橙色显示),现在有4种数据: 域管理员:domainAdmin 组织管理员:orgAdmin 组管理员:groupAdmin 一线员工:phoneAdmin 权限控制可以 ...

  8. EF Code First学习笔记:数据库创建

    控制数据库的位置 默认情况下,数据库是创建在localhost\SQLEXPRESS服务器上,并且默认的数据库名为命名空间+context类名,例如我们前面的BreakAway.BreakAwayCo ...

  9. spring2.5IOC控制反转详解

    spring2.5IOC控制反转详解 19. 五 / J2EE / 一条评论   基本的代码结构 1 IOC包下 基本的spring创建对象 将类添加到配置文件中,由容器创建. Source code ...

随机推荐

  1. vue vue-route 传参 $route.params

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. Oracle性能优化2- 依据场景选择技术

    1.索引的坏处 索引可以加快查询效率,但是使用不当,会造成插入性能很低 drop table test1 purge; drop table test2 purge; drop table test3 ...

  3. vs2015未能计算子级

    数据源 属性里边值  设置出现问题

  4. if 循环的深入理解 哈希表的一种应用

       哈希表的值作为一个颜色容器,值默认为标识1, 表示未曾用过,若用过标识为0:  1: 程序第一步    遍历哈希表,查找标识为1 未曾用过的颜色 我用了这个: string colorno_us ...

  5. poj 2785 让和为0 暴力&二分

    题目链接:http://poj.org/problem?id=2785 大意是输入一个n行四列的矩阵,每一列取一个数,就是四个数,求有多少种着四个数相加和为0的情况 首先脑海里想到的第一思维必然是一个 ...

  6. Codeforces 782C. Andryusha and Colored Balloons 搜索

    C. Andryusha and Colored Balloons time limit per test:2 seconds memory limit per test:256 megabytes ...

  7. The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone问题解决

    从错误即可知道是时区的错误,因此只要将时区设置为你当前系统时区即可 因此使用root用户登录mysql,按照如下图所示操作即可. 把时区设置为所在地时区(即东八区的时区)后,再连接数据库就可以了

  8. spring boot 启动自动跳到 断点 throw new SilentExitException

    项目 debug 启动,自动跳到 断点 ,而且就算F8 ,项目还是停止启动. 百度了一下,答案都是 Eclipse -> Preferences ->Java ->Debug去掉&q ...

  9. IOS初级:AFNetworking

    狗 日的,第三方框架真j8难搞 1.为什么NS_ASSUME_NONNULL_BEGIN在6.2报错,你他么的还挑IDE,你这是什么态度? 2.还有,你他么的自动给老子转json了,有问过我么? #i ...

  10. Spring Boot学习笔记:ApplicationEvent和ApplicationEventListener事件监听

    采用事件监听的好处 以用户注册的业务逻辑为例,用户在填写完信息表单后,提交信息到后台,后台对用户信息进行处理,然后给用户返回处理结果的信息. 如上图所示,用户在注册时,后台需要处理一些系列流程,实际业 ...