Code First 属性详解
https://www.cnblogs.com/xszjk/articles/5517507.html
下面解释每个配置的作用
Table :用于指定生成表的表名、架构信息。
Column :用于指定生成数据表的列信息,如列名、数据类型、顺序等。
Key :用于指定任何名称的属性作为主键列并且默认将此列作为标识列(如果不想默认生成标识可以指定“DatabaseGenerated”属性的值为“None”),如果不指定此标记属性,将根据EF默认约定创建主键。如上代码指定“No”为“Employee”的主键。
Required :用户指定非空列,如上面的“BirthDay”创建列之后为“not null”列。
MinLength 、 MaxLength :指定字段长度(此属性通常可以用户客户端验证),例如上面“Title”定义成了“nvarchar(30)”。
ComplexType :用于标记复杂类型,对于包含复杂类型数据属性的类在生成数据表时复杂类型中每个属性都将作为其中一列。
DatabaseGenerated :用于指定数据库字段生成列,此类EF将不会直接更新。可以指定为计算列、标识列和非数据库生成列(例如给主键列指定此属性为“None”则不会生成标识列)。需要注意的是如果使用Code First字段生成数据库,那么此属性仅仅可以用于byte、timestamp列上,否则请应用在已经存在数据库的情况下,因为Code First无法判定生成具体计算列的公式(至少目前Code First还不支持公式配置)。
Identity:自增长 None:不处理 Computed:表示这一列是计算列。
NotMapped :用户指定非映射列,标记此属性的列将不会在数据库中生成相应的列,例如上面的“PhotoPath ”没有在数据库中生成具体列,实际使用中它可能是通过其他具体规则得到的。
ConcurrencyCheck :用于进行并发检查,当一个用户A获得实体后通常会与数据库断开,此时如果另一个用户B也获得了实体并进行了修改,那么当A再进行更新时如果进行了“ConcurrencyCheck”标记则会进行并发检查,并根据原始值判断该实体是否存在,如果不存在则抛出异常。
TimeStamp :用于指定时间戳列,一个实体只能有一个TimeStamp列。在EF中TimeStamp是另一种并发控制方式,当EF遇到TimeStamp列会自动配置 “ConcurrencyCheck”及“DatabaseGenerated.Computed”来控制并发(通常我们推荐使用此方法)。
timestamp的精度更高,date精确到秒,而timestamp则精确到秒后三位。
ForeignKey :用于指定外键列,我们知道按照上面提到的默认约定第三条,当我们在“Order”中定义了“Customer”属性后,如果定义“CustomerID” 属性(当然还有其他形式,大家可以按照声明说的默认约定3进行测试),那么EF会在“Order”表中创建一个“CustomerID”列并建立与“Customer”表的外键关系。但是如果像上面定义“CustomerNo”属性并且不指定“ForeignKey”标记的话将达不到我们的预期,EF将默认创建一个“Customer_CustomerID”列并创建与“Customer”的外键约束,同时创建一个“CustomerNo”列。当然解决的方式大家已经看到了那就是给导航属性“Customer”指定“ForegnKey”标记并且指定外键列为“CustomerNo”(注意虽然在“Customer”中不定义“Order的导航属性”生成的表中也并没用任何问题,但是我们推荐您定义相应的导航属性)。
InverseProperty :用于定义多重外键关系约束。我们在EF中通过导航属性定义主外键关系,但是当主表中有两个外键约束时可能仅仅通过添加相应的导航属性就无法完成了,例如上面“Order”中“DeliverPerson”和“CheckPerson”均为“Employee”类型,按我们的预期当然是在生成“Order”表时生成两个外键列并创建与“Employee”外键约束关系,但是如果没有在“Employee”类的“DeliverOrder”和“CheckOrder”属性上标记 “InverseProperty”属性EF是无法识别这种关系的(具体结果可以看下图),当然解决方式就是在对应导航属性中标记“InverseProperty”并指定对于的列名。
//主键Primary key如果类的属性名为“ID”(不区分大小写)或类名的后面跟有“ID”,则 Code First 会推断该属性是主键。如果主键属性的类型为数值或 GUID,则将其配置为标识列。
导航属性(Navigationproperty)提供了一种在两个实体类型之间导航关系的方法。针对对象参与到其中的每个关系,各对象均可以具有导航属性。使用导航属性,可以 在两个方向上导航和管理关系,返回引用对象(如果多重性为一或者零或一)或集合(如果多重性为多)
如果依赖实体上的外键不能为 Null,则 CodeFirst 对关系设置级联删除。如果依赖实体上的外键可以为 Null,则Code First 不对关系设置级联删除,并且在删除主体时,会将该外键设置为 Null。通过使用 Fluent API,可以覆盖由约定检测的多重性和级联删除行为。
数据库创建是由策略来控制的,有如下四种策略:
1. CreateDatabaseIfNotExists:这是默认的策略。如果数据库不存在,那么就创建数据库。但是如果数据库存在了,而且实体发生了变化,就会出现常。
2. DropCreateDatabaseIfModelChanges:此策略表明,如果模型变化了,数据库就会被重新创建,原来的数据库被删除掉了。
3. DropCreateDatabaseAlways:此策略表示,每次运行程序都会重新创建数据库,这在开发和调试的时候非常有用。
4. 自定制数据库策略:可以自己实现IDatabaseInitializer来创建自己的策略。或者从已有的实现了IDatabaseInitializer接口的类派生。 如下示例显示了如何应用数据库创建策略:
public class UserManContext : DbContext {
public UserManContext() : base("USMDBConnectionString") {
Database.SetInitializer(new CreateDatabaseIfNotExists());
}
}
[MetadataType(typeof(Blog))] //将元数据类与实体分部类关联
[DisplayName(“博客”)] //描述
[Table("Blogs",Schema="dbo")] //定义表名
public class Blog {
[Key] //主键
public int PrimaryTrackingKey { get; set; }
[Required] //不为空
[DefaultValue("默认值")]//另外一种方式类的构造函数中进行赋值Title="默认值";
public string Title { get; set; }
[ConcurrencyCheck,MaxLength(10), MinLength(5)] //长度约束
public string BloggerName { get;set; }
[NotMapped] //注释来标记不映射到数据库的所有属性
public string BlogCode{
get{
returnTitle.Substring(0, 1) + ":" +BloggerName.Substring(0, 1);
}
}
[Timestamp]
publicByte[] TimeStamp { get;set; }
public virtual ICollection Posts { get;set; } // ICollection可用IList/List/Collection
}
public class Post {
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get;set; }
public string Content { get;set; }
[Range(1,100,ErrorMessage=”{0}在{1}与{2}之间”)] //带参数的错误信息
public int SkipNum{get;set;}
public int BlogId { get; set; }
[ForeignKey("BlogId")]
Public virtual ICollectionComments { get; set;}
}
[ComplexType] //复杂类型公用内容集合可做为某一个类的一部分
public class BlogDetails{
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? DateCreated { get;set; }
[MaxLength(250)]
[Column("BlogDescription",TypeName = "ntext")]
public string Description { get;set; }
}
publicclass Person {
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("CreatedBy")]
public ListPostsWritten { get; set;}
[InverseProperty("UpdatedBy")]
Public ListPostsUpdated { get; set;}
}
[InverseProperty]
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int ForeignKeyCurrent { get; set; }
public int ForeignKeyPrevious { get; set; }
[ForeignKey("ForeignKeyCurrent")]
public Standard CurrentStandard { get; set; }
[ForeignKey("ForeignKeyPrevious")]
public Standard PriviousStandard { get; set; }
}
public class Standard
{
public int StandardId { get; set; }
public string StandardName { get; set; }
/// InverseProperty,反属性特性
[InverseProperty("CurrentStandard")]
public ICollection CurrentStudent { get; set; }
/// InverseProperty
/// InverseProperty,反属性特性
[InverseProperty("PriviousStandard")]
public virtual ICollection PreviousStudent { get; set; } //virtual延迟加载
}
DbContext 使用DbSet 属性 Code First 示例中显示的常见情况是让 DbContext 为模型实体类型使用公共自动DbSet 属性。
例如:可使用 IDbSet 接口替代 DbSet
publicclass BloggingContext: DbContext {
public DbSetBlogs { get; set;}
public DbSetPosts { get; set;}
}
DbContext使用只读set属性
public DbSetBlogs{
get{
return Set();
}
}
同步FK 和导航属性之间的更改
DbSet.Add
context.Blogs.Add(blog);
DbSet.Find
var post =context.Posts.Find(主键);
DbSet.Remove
DbSet.Local
DbContext.SaveChanges
DbSet.Attach
context.Blogs.Attach(existingBlog);
DbContext.GetValidationErrors
DbContext.Entry
context.Entry(blog).State =EntityState.Added;
context.Entry(existingBlog).State =EntityState.Unchanged;
context.Entry(existingBlog).State =EntityState.Modified;
DbChangeTracker.Entries