转自: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了。