首页
视频
资源
登录
原
EF6数据库表的创建和迁移
6803
人阅读
2021/6/3 23:14
总访问:
2591113
评论:
0
收藏:
0
手机
分类:
.net后台框架
![](https://img.tnblog.net/arcimg/hb/f170e5179e8f44c18b1cd575d5276c66.png) >#EF6数据库连接和初始化策略 [TOC] 数据库连接 ------------ tn>创建一个控制台应用程序 ###安装EF6 ![](https://img.tnblog.net/arcimg/hb/c4defa8b4b744514b0ed8373dfa255d8.png) ###创建代码内容 >Blog类 ```csharp public class Blog { public int Id { get; set; } public string Name { get; set; } public string Url { get; set; } public DateTime? CreatedTime { get; set; } public double Double { get; set; } public float Float { get; set; } } ``` tn>创建一个EF上下文实例类,我们可以称为会话,并且为每一个模型公开一个`IDbSet`。 >EfDbContext类 ```csharp public class EfDbContext:DbContext { public IDbSet<Blog> Blogs { get; set; } } ``` ###添加连接字符串 tn>在VS工具栏中根据下面的路径连接并创建数据库的实例 ```mermaid graph LR A(视图) -->B(服务器资源管理器) B(服务器资源管理器) -->C(右键创建新的数据库) ``` ![](https://img.tnblog.net/arcimg/hb/efb176c77d34409c81fd190159826695.png) ![](https://img.tnblog.net/arcimg/hb/ecbbaf82131e4f85959a09958b41a9cf.png) ![](https://img.tnblog.net/arcimg/hb/52d2ec7c04a74b169192d14153bb79f3.png) tn>将连接字符串放到`App.config`或`Web.config`中,并将连接字符串取名为`ConnectionString` ```xml <!-- 连接字符串 --> <connectionStrings> <add name="ConnectionString" connectionString="Data Source=CNLT1824\CNLT1824;Initial Catalog=EF6Learning;User ID=sa;Password=cn@49akING;Pooling=False" providerName="System.Data.SqlClient" /> </connectionStrings> ``` ![](https://img.tnblog.net/arcimg/hb/82b84929b409471e9330fc07148e0a88.png) >为`EfDbContext`上下文实例指定连接字符串 ```csharp public class EfDbContext:DbContext { public EfDbContext():base("name=ConnectionString"){} public IDbSet<Blog> Blogs { get; set; } } ``` ###添加数据 ```csharp class Program { static void Main(string[] args) { using (var efDbContext = new EfDbContext()) { efDbContext.Blogs.Add(new Blog() { Name = "Hemingyang", Url = "http://www.tnblog.net" }); efDbContext.SaveChanges(); } } } ``` tn>运行后,在数据库中已经创建好了相应的表。 ![](https://img.tnblog.net/arcimg/hb/a6d17b76529643d4b5d78b9367612e0b.png) 数据库初始化策略 ------------ ### 代码初始化策略 tn>在EF中初始化数据库有三种策略,需要在EF上下文派生类的构造函数中定义。 >- 如果数据库不存在,就创建: ```csharp Database.SetInitializer(new CreateDatabaseIfNotExists<EfDbContext>()); ``` - 总是创建数据库,无论存在与否 ```csharp Database.SetInitializer(new DropCreateDatabaseAlways<EfDbContext>()); ``` - 如果EF检测到数据库模型发生了改变,将更新模型: ```csharp Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EfDbContext>()); ``` ### 配置文件设置初始化策略 ```xml <appSettings> <add key="DatabaseInitializerForTypeEntityFramework_CreateDbContext.EfDbContext, EntityFramework_CreateDbContext" value="System.Data.Entity.DropCreateDatabaseAlways, EntityFramework"/> </appSettings> ``` tn>上述 `key` 值 `DatabaseInitializerForType`后面紧跟命名空间+DbConext派生类,其余两种初始化策略同理。如果想要禁用数据库初始化策略,那么在`DbContext`派生类和配置文件中该如何配置呢?代码如下: ```csharp public class EfDbContext:DbContext { public EfDbContext():base("name=ConnectionString"){ Database.SetInitializer<EfDbContext>(null); } public IDbSet<Blog> Blogs { get; set; } } ``` ```xml <appSettings> <add key="DatabaseInitializerForTypeEntityFramework_CreateDbContext.EfDbContext, EntityFramework_CreateDbContext" value="Disabled"/> </appSettings> ``` >#约定 类型发现 ------------ ### 创建Department类 >我们先定义一个`Department`类,并在`EfDbContext`上下文中通过定义一个`IDbSet`属性来进行暴露模型的一部分,当然我们还可以通过`Fluent API`来忽略将其映射到数据库中。 ```csharp public class Department { /// <summary> /// 主键 /// </summary> public int DepartmentID { get; set; } public string Name { get; set; } } ``` ```csharp public class EfDbContext:DbContext { public EfDbContext():base("name=ConnectionString"){ // 总是创建数据库,无论存在与否 Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EfDbContext>()); } public IDbSet<Blog> Blogs { get; set; } public IDbSet<Department> Departments { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // 忽略映射 modelBuilder.Ignore<Department>(); base.OnModelCreating(modelBuilder); } } ``` ```csharp // 测试代码 using (var efDbContext = new EfDbContext()) { efDbContext.Departments.Add(new Department() { Name = "Hemingyang" }); efDbContext.SaveChanges(); } ``` ![](https://img.tnblog.net/arcimg/hb/94847c80fe414619b3e6b9f0c0a11ab8.png) >接着我们在数据库中,发现并没有相关的表创建 ![](https://img.tnblog.net/arcimg/hb/42864a67f8b145b48a765a2b1d677477.png) 主键约定 ------------ tn>Code First 根据模型中定义的ID,或者是以类名加上ID的属性,推断这样的属性为主键,如果主键为`int`或者`guid`类型,那么主键将会被映射成标识列(自增长)。在`Department`中的`DepartmentID`将被映射成主键且自增长 当我们定义一个学生地址类(`StudentAddress`),在里面定义两个ID试试 ```csharp public class StudentAddress { public int AddressID { get; set; } public int StudentID { get; set; } } ``` ```csharp public class EfDbContext:DbContext { public EfDbContext():base("name=ConnectionString"){ // 总是创建数据库,无论存在与否 Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EfDbContext>()); } public IDbSet<Blog> Blogs { get; set; } public IDbSet<Department> Departments { get; set; } public IDbSet<StudentAddress> StudentAddresss { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // modelBuilder.Ignore<Department>(); base.OnModelCreating(modelBuilder); } } ``` ![](https://img.tnblog.net/arcimg/hb/0082add920bb4a30937dc06755d6ce52.png) tn>上述错误信息表明`StudentAddress`未定义主键,主键必须是**ID(不区分大小写)或者是类名加上ID**,`StudentAddress`主键的定义必须是`StudentAddressID`或者`ID`,否者将抛出异常。 ```csharp public class StudentAddress { public int StudentAddressID { get; set; } public int StudentID { get; set; } } ``` ![](https://img.tnblog.net/arcimg/hb/7275ef3e21ee417aaec958c159b940ac.png) 关系约定 ------------ >在EF中,导航属性可以为两个实体模型建立联系。 - 定义导航属性 - 有依赖对象的类型上建立外键属性 tn>导航属性的定义如下 | | | ------------ | | <导航属性名称><主体主键属性名称> | | <主体类名><主键属性名称> | | <主体主键属性名称> | >若有多个匹配,则根据上述列举的优先级进行外键属性映射,同样对于外键属性的检测不区分大小写,当推断出外键属性后,接着根据外键属性的可空性来推断关系的形式,如果外键属性为空,那么模型关系将配置为可选(Code First 不会在关系上配置级联删除),也就是说当主体被删除时,外键将被设置为级联删除。例如: ```csharp public class Department { /// <summary> /// 主键 /// </summary> public int DepartmentID { get; set; } public string Name { get; set; } public virtual ICollection<Course> Courses { get; set; } } public class Course { /// <summary> /// 主键 /// </summary> public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } /// <summary> /// 外键 /// </summary> public int DepartmentID { get; set; } /// <summary> /// 导航属性 /// </summary> public virtual Department Department { get; set; } } ``` 复杂类型的约定 ------------ tn>当Code First不能推断出一个模型有没有主键且主键没有通过Data Annotations或者Fluent API来进行手动配置时,该类型将自动被配置为复杂类型,复杂类型检测同时也要求该类型没有引用实体类型的属性,例如: ```csharp public class Order { public int Id { get; set; } public string Name { get; set; } public Address OrderAddress { get; set; } public class Address { public string Street; public string Region; public string Country; } } ``` tn>我们可以这样理解复杂类型:一个复杂类型(Address)作为已存在对象的属性,但是会将复杂类型的列映射到已存在的表(Order)表中,已存在的表包含这些列而不是将复杂类型映射成另外单独的一张表(单独映射成表还需要主键)。 我们可以进行如下映射: ```csharp public IDbSet<Order> Orders { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Order>().ToTable("Orders"); modelBuilder.ComplexType<Address>(); base.OnModelCreating(modelBuilder); } ``` tn>虽然按照官方的说法去进行映射该复杂类型,但貌似在EF Code First中并不可取。 自定义约定 ------------ tn>从EF 6.0后,我们可以通过自定义约定来确定所有实体和属性的规则。 ###全局约定 >如果我们希望在项目中,所有实体的主键都叫ID,在重写的`OnModelCreating`方法里面我们可以这样写: ```csharp modelBuilder.Properties() .Where(x => x.Name == "Id") .Configure(p => p.IsKey()); ``` >还可以通过自定义类进行约定 ```csharp public class CustomKeyConvention:Convention { public CustomKeyConvention() { Properties() .Where(x => x.Name == "Id") .Configure(p => p.IsKey()); } } ``` ```csharp protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Add<CustomKeyConvention>(); } ``` >还可以通过`Properties`方法对所有模型中属性为`decimal`的类型,配置位数为10位保留2位小数进行全局映射: ```csharp modelBuilder.Properties<decimal>() .Configure(config => config.HasPrecision(10, 2)); ``` ### 执行约定 >当我们对多个属性进行相同的约定配置时,最后一个约定将覆盖前面所有相同的约定。举例: ```csharp modelBuilder.Properties<string>() .Configure(c => c.HasMaxLength(500)); modelBuilder.Properties<string>() .Where(x => x.Name == "Name") .Configure(c => c.HasMaxLength(300)); //除了名为Name的字符串长度300以外,其他字符串长度都为500 ``` ### 内置约定 >为了保证自定义约定执行规则的先后顺序,提供了两个方法`AddBefore`和`AddAfter` ```csharp modelBuilder.Conventions.Add<CustomKeyConvention>(); // 定义在CustomKeyConvention约定之前执行该约定 modelBuilder.Conventions.AddAfter<CustomKeyConvention>(new DateTime2Convention()); // 定义在CustomKeyConvention约定之后执行该约定 modelBuilder.Conventions.AddBefore<CustomKeyConvention>(new DateTime2Convention()); ``` tn>关于`DateTime2Convention`将在下面的自定义类约定呈现代码 ### 自定义类约定 >自定义约定包含一个约定接口(`IConvention`),`IConceptualModelConvention`为概念模型接口,在模型创建后被调用。`IStoreModelConvention`接口为存储模型接口,在模型创建后用于操作对模型的存储,自定义的约定都必须在重载的`OnModelCreating`方法中显示配置。若我们把Datetime类型转换为datetime2,则进行如下操作: ```csharp public class DateTime2Convention : Convention { public DateTime2Convention() { Properties<DateTime>() .Configure(c => c.HasColumnType("datetime2")); } } ``` ```csharp modelBuilder.Conventions.Add<DateTime2Convention>(); ``` ### 自定义类型约束 tn>通常不同的公司对命名都有一定的规范,所以我们也可以为不同的实体对象定制一些规则。这里我们以下划线隔开,对此我们可以通过调用`Types`方法更改表名约定。 ```csharp protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Add<CustomKeyConvention>(); modelBuilder.Types() .Configure(c => c.ToTable(GetTableName<Blog>())); } private string GetTableName<T>() { return GetTableName(typeof(T)); } private string GetTableName(Type type) { var result = Regex.Replace(type.Name, ".[A-Z]", m => m.Value[0] + "_" + m.Value[1]); return result.ToLower(); } ``` ![](https://img.tnblog.net/arcimg/hb/9544757a84f94261a17c9943365a89aa.png) ### 自定义特性 tn>约定最大的一个用途在于:当配置模型时,启动的新的属性。
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}
尘叶心繁
这一世以无限游戏为使命!
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
.net后台框架
168篇
linux
17篇
linux中cve
1篇
windows中cve
0篇
资源分享
10篇
Win32
3篇
前端
28篇
传说中的c
4篇
Xamarin
9篇
docker
15篇
容器编排
101篇
grpc
4篇
Go
15篇
yaml模板
1篇
理论
2篇
更多
Sqlserver
4篇
云产品
39篇
git
3篇
Unity
1篇
考证
2篇
RabbitMq
23篇
Harbor
1篇
Ansible
8篇
Jenkins
17篇
Vue
1篇
Ids4
18篇
istio
1篇
架构
2篇
网络
7篇
windbg
4篇
AI
18篇
threejs
2篇
人物
1篇
嵌入式
2篇
python
13篇
HuggingFace
8篇
pytorch
9篇
opencv
6篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2024
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术