
Entity Framework Code First属性映射约定中“约定”一词,在原文版中为“Convention”,翻译成约定或许有些不好理解,这也是网上比较大多数的翻译,我们就当这是Entity Framework的一些使用“规则”,这样或许更好理解一些。
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API。本文中采用创建Product类为例来说明tity Framework Code First属性映射约定的具体方式。
1、表名及所有者
在默认约定的情况下,Entity Framework Code First创建的表名是根据类名的英语复数形式,创建的表所有者为dbo,可以通过重写约定来指定表名及表的所有者。
1>、Data Annotation方式
在使用Data Annotation方式进行Entity Framework Code First与数据库映射之前,需要先添加命名空间引用。
1
|
using System.ComponentModel.DataAnnotations.Schema;
|
为类配置对应表名:
1
2
|
[Table( "Product" )]
public class Product
|
为类配置对应表名并指定表的所有者:
1
2
|
[Table( "Product" , Schema = "dbo" )]
public class Product
|
2>、Fluent API方式
Fluent API实现配置Entity Framework Code First与数据库映射关系主要是通过继承DbContext并重写其中的OnModelCreating方法来进行的。在本文中新建类文件PortalContext.cs继承DbContext。
在继承DbContext之前,添加命名空间引用。
1
|
using System.Data.Entity;
|
重写OnModelCreating方法,配置类对应于数据库中的表名:
1
2
3
4
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Product>().ToTable( "Product" );
} |
重写OnModelCreating方法,配置类对应于数据库中的表名,并指定表的所有者:
1
2
3
4
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Product>().ToTable( "Product" , "dbo" );
} |
到此处PortalContext.cs的完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using Portal.Entities;
namespace Portal
{ public class PortalContext : DbContext
{
static PortalContext()
{
Database.SetInitializer( new DropCreateDatabaseIfModelChanges<PortalContext>());
}
public PortalContext()
: base ( "name=PortalContext" )
{
}
public DbSet<Product> Products { get ; set ; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().ToTable( "Product" , "dbo" );
}
}
} |
2、字段名、长度、数据类型及是否可空
在默认约定的情况下,Entity Framework Code First创建的列名与类的属性名相同,可以根据需要进行重新指定类属性与列名之间的映射关系。
1>、Data Annotation方式
1
2
3
4
5
|
[Column( "ProductID" )]
public int ProductID { get ; set ; }
[MaxLength(100)] [Required, Column( "ProductName" )]
public string ProductName { get ; set ; }
|
在使用Required特性(Attribute)设置字段不允许为空时,需要添加命名空间引用:
1
|
using System.ComponentModel.DataAnnotations;
|
2>、Fluent API方式
1
2
3
4
5
6
7
8
9
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Product>().Property(t => t.ProductID)
.HasColumnName( "ProductID" );
modelBuilder.Entity<Product>().Property(t => t.ProductName)
.IsRequired()
.HasColumnName( "ProductName" )
.HasMaxLength(100); } |
在默认情况下,int类型的属性生成的列名对应SQL SERVER列int类型;而String类型的属性则对应SQL SERVER列的NVARCHAR类型。若类的字符串类型属性未设置MaxLength,则生成对应的列类型为NVARCHAR(MAX)。
为属性指定对应的SQL SERVER数据类型:
1
2
3
4
5
|
[Column( "UnitPrice" , TypeName = "MONEY" )]
public decimal UnitPrice { get ; set ; }
modelBuilder.Entity<Product>().Property(t => t.UnitPrice) .HasColumnName( "UnitPrice" )
.HasColumnType( "MONEY" );
|
到此步,Product.cs类文件的完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Portal.Entities
{ [Table( "Product" , Schema = "dbo" )]
public class Product
{
[Column( "ProductID" )]
public int ProductID { get ; set ; }
[MaxLength(100)]
[Required, Column( "ProductName" )]
public string ProductName { get ; set ; }
[Column( "UnitPrice" , TypeName = "MONEY" )]
public decimal UnitPrice { get ; set ; }
}
} |
3、主键
Entity Framework Code First的默认主键约束:属性名为[ID]或[类名 + ID]。如在Product类中,Entity Framework Code First会根据默认约定将类中名称为ID或ProductID的属性设置为主键。Entity Framework Code First主键的默认约定也一样可以进行重写,重新根据需要进行设置。
1>、Data Annotation方式
1
2
3
|
[Key] [Column( "ProductID" )]
public int ProductID { get ; set ; }
|
2>、Fluent API方式
1
2
3
4
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Product>().HasKey(t => t.ProductID);
} |
若一个表有多个主键时:
1
2
3
4
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Product>().HasKey(t => new { t.KeyID, t.CandidateID });
} |
4、数据库自动生成字段值
Entity Framework Code First对于int类型的主键,会自动的设置其为自动增长列。但有时我们确实不需是自动增长的,可以通过以下方式进行取消自动增长。
1>、Data Annotation方式
1
2
3
4
|
[Key] [Column( "ProductID" )]
[DatabaseGenerated(DatabaseGeneratedOption.None)] public int ProductID { get ; set ; }
|
2>、Fluent API方式
1
2
3
4
5
6
7
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Product>().HasKey(t => t.ProductID);
modelBuilder.Entity<Product>().Property(t => t.ProductID)
.HasColumnName( "ProductID" )
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
} |
5、数字类型长度及精度
在Product类中,UnitPrice表示单价,对于价格类的字段,我们通常会希望其保留2为小数。这时可以使用Fluent API进行设置,且Data Annotation不支持该设置。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().Property(t => t.UnitPrice)
.HasColumnName("UnitPrice")
.HasPrecision(18, 2);
}
6、非数据库字段属性
在类中,如果有一些属性不需要映射到对应生成的数据表中,可以通过以下方式设置。
1>、Data Annotation方式
[NotMapped]
public string Remark { get; set; }
2>、Fluent API方式
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().Ignore(t => t.Remark);
}
7、Fluent API配置Configuration映射类
在使用Fluent API进行Entity Framework Code First数据库映射时,除了以上的在重写OnModelCreating方法中直接对Entity进行配置之外,也可以对Configurations进行配置。这时可以先写一个单独的类,将数据表的全部映射要求都写在构造函数中。
ProductMap.cs类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using Portal.Entities;
namespace Portal.Mapping
{ public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
// Primary Key
this .HasKey(t => t.ProductID);
// Properties
this .Property(t => t.ProductID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this .Property(t => t.ProductName)
.IsRequired()
.HasMaxLength(100);
// Table & Column Mappings
this .ToTable( "Product" );
this .Property(t => t.ProductID).HasColumnName( "ProductID" );
this .Property(t => t.ProductName).HasColumnName( "ProductName" );
this .Property(t => t.UnitPrice)
.HasColumnName( "UnitPrice" )
.HasPrecision(18, 2);
}
}
} |
有了上面的映射类之后,在重写OnModelCreating方法中则可以直接调用映射类,从而减少了OnModelCreating方法的复杂度,同时也增强了代码维护的可读性。
1
2
3
4
|
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Configurations.Add( new ProductMap());
}
|