在EF7中,创建一个模型是非常重要的步骤。本文将使用微软官方文档中的指南,来学习EF7中的创建模型篇,外加一点点个人理解。
在 EF7 中,你需要使用 modelBuilder.Entity
如果你的数据库中有多个模式(schema),你可以使用 ToTable() 方法的另一个重载版本来指定表所属的架构。如果你想要为生成的表添加注释,可以使用 HasComment() 方法。如果你不想将某个类映射到数据库中的表。我们可以使用 modelBuilder.Entity
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("Blogs", schema: "dbo") //.ToTable("Blogs");
.HasComment("This table contains blog posts.");
modelBuilder.Ignore<Address>();
}
在 EF7 中,你可以将一个类型映射到多个表中。这种情况通常发生在你有一组具有相似属性的类型,这些属性在不同的表中都需要使用。在这种情况下,你可以使用 ModelBuilder.SharedTypeEntity() 方法来创建一个实体类型,并将其映射到多个表中。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var addressEntity = modelBuilder.SharedTypeEntity<Address>("Address");
addressEntity.ToTable("CustomerBillingAddresses");
addressEntity.ToTable("CustomerShippingAddresses");
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.BillingAddress, b =>
{
b.WithOwner().HasForeignKey("BillingAddressId");
b.ToTable("CustomerBillingAddresses");
});
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.ShippingAddress, b =>
{
b.WithOwner().HasForeignKey("ShippingAddressId");
b.ToTable("CustomerShippingAddresses");
});
}
在上面的代码中,我们首先使用 ModelBuilder.SharedTypeEntity() 方法创建一个名为 Address 的实体类型。然后,我们使用 ToTable() 方法将该实体类型映射到多个表中。接下来,我们使用 OwnsOne() 方法来将 BillingAddress 和 ShippingAddress 属性映射到具有相应名称的表中。注意,我们还使用了 HasForeignKey() 方法来指定外键的名称。
使用共享类型实体类型可以使你的代码更加简洁,并提高可维护性。通过使用共享类型实体类型,你可以将一个类型映射到多个表中,而不必在每个实体类型中都定义相同的映射代码。
如果要排除实体属性,可以使用Ignore()方法。
modelBuilder.Entity<Person>().Ignore(p => p.Age)
定义列名。例如,下面的代码将为Person类中的LastName属性定义列名。
modelBuilder.Entity<Person>().Property(p => p.LastName).HasColumnName("Last_Name")
定义列注释。例如,下面的代码将为Person类中的LastName属性定义注释。
modelBuilder.Entity<Person>().Property(p => p.LastName).HasComment("The last name of the person")
定义列排序规则。例如,下面的代码将为Person类中的LastName属性定义排序规则。
modelBuilder.Entity<Person>().Property(p => p.LastName).UseCollation("SQL_Latin1_General_CP1_CI_AS");
定义列的数据类型(和数据库一致即可)。例如,下面的代码将为Person类中的Age属性定义为int类型:
modelBuilder.Entity<Person>().Property(p => p.Age).HasColumnType("int");
定义列的最大长度。例如,下面的代码将为Person类中的FirstName属性定义为50个字符的最大长度:
modelBuilder.Entity<Person>().Property(p => p.FirstName).HasMaxLength(50);
定义列的精度和小数位数。例如,下面的代码将为Person类中的Height属性定义为2位小数的精度:
modelBuilder.Entity<Person>().Property(p => p.Height).HasPrecision(5, 2);
定义是否为必需或可选属性。例如,下面的代码将为Person类中的FirstName属性定义为必需属性:
modelBuilder.Entity<Person>().Property(p => p.FirstName).IsRequired();
定义列在表中的顺序。例如,下面的代码将为Person类中的FirstName属性定义为表中第二个列:
modelBuilder.Entity<Person>().Property(p => p.Id).HasColumnOrder(1);
modelBuilder.Entity<Person>().Property(p => p.FirstName).HasColumnOrder(2);
定义主键。根据约定,名为 Id 或 <类型名称>Id 的属性将被配置为实体的主键。
internal class User
{
public string Id { get; set; } // 主键
public string Name { get; set; }
}
如果我们不想使用默认约定规则,可以自定义规则。下面的代码将指定User类的Id属性作为主键并且重写设置主键的名称:
modelBuilder.Entity<Person>().HasKey(p => p.Id).HasName("UserId");
在 MySQL 中,主键 Id 不是有顺序的时候,可能会导致新增性能下降的原因是,MySQL 默认使用 B-tree 索引来实现主键索引,如果主键 Id 是无序的,那么在插入数据时,MySQL 需要不断寻找合适的位置来插入新数据,这可能会导致 B-tree 索引不断被调整,从而影响插入性能。相反,如果主键 Id 是有序的,MySQL 可以更快速地找到要插入的位置,从而提高插入性能。
有些程序可能会有延迟,导致数据库插入是非有序的。场景:假如我们先生成Id再处理业务逻辑。有两个线程,同时并发请求。第一个线程生成好Id 是 3,处理下面业务逻辑时发生了大概五毫秒的延迟。第二个线程也生成好了Id是 4,处理下面业务逻辑时没有延迟,就通过了。所以第二个线程,先进数据库插入Id为4。第一个线程因为有延迟来慢了一步,插入的Id是3。数据库Id就变成无序的了。
对于非复合数字和 GUID 主键,EF Core 根据约定设置值生成。
EF7中 Guid 是基于时间的算法精确到纳秒。因为一毫秒等于一百万纳秒,所以EF7的Guid一毫秒可以产生一百万的Id。
优点:不可预测、有序(添加性能高)、在支持Guid(uuid)的数据库中(查询性能高)、开箱即用。
缺点:(这是可以忽略不计的事情)并发中在同一纳秒内,产生的Id是会重复的。有时钟回拨问题。太长。
EF7中的Guid有序算法比雪花算法更好。
使用方式见这篇文章:《EF7创建模型值生成篇》。
复合键是指将多个列作为主键的一种设计模式,这些列在组合起来时才能唯一标识一条记录。相对于单一键,复合键更加灵活,可以更准确地描述实体之间的关系。例如,在一个订单系统中,一个订单可能由多个产品组成,此时可以使用复合键来标识订单编号和产品编号的组合,以确保每个订单中的每个产品都是唯一的。
使用复合键的优点主要有两点。首先,它提供了更准确的数据描述,特别是在处理多对多关系时,可以更准确地表示关系的唯一性。其次,使用复合键可以提高查询效率,因为复合键可以利用数据库的索引机制,快速定位和访问数据。
然而,复合键也存在一些缺点。首先,它增加了开发的复杂性,需要更多的设计和规划。其次,在使用ORM框架时,如EF7,复合键的使用需要特殊的处理,例如在Fluent API中进行配置。最后,复合键在某些情况下可能会导致性能问题,例如在大型数据库中,使用复合键可能会影响查询性能。
在使用EF7时,可以通过Fluent API来配置复合键。以一个用户角色表为例,可以使用以下代码定义一个由用户Id和角色Id的复合主键:
internal class UserRole
{
public string UserId { get; set; }
public string RoleId { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRole>().HasKey(c => new { c.UserId, c.RoleId });
}
}
在这个示例中,使用Fluent API中的HasKey方法来定义复合主键。由于复合键是由多个属性组成的,因此需要将它们放在一个匿名类型中作为参数传递给HasKey方法。
MySql为例:当使用复合键并且确保关联数据都已经存在时,插入新数据时可能不会对性能产生太大影响。这是因为在 MySQL 中,使用复合键时,MySQL 会同时使用所有列生成 B-tree 索引,从而提高查询和插入的性能。在插入数据时,如果已经确保了关联数据的存在,那么 MySQL 可以更快速地插入新数据并生成新的索引,从而提高插入性能。
但是,需要注意,如果你的表结构复杂,或者在插入数据时没有正确使用索引,那么复合键仍然可能会影响插入性能。
为什么要使用EF7备选键?
在数据库中,主键通常用于唯一标识和检索实体对象。但是,有些情况下可能需要使用备选键来标识实体对象。例如,当主键不适合用作某些查询时,使用备选键可以提高数据库的性能和灵活性。
优点:
缺点:
为什么有这些优点和缺点?
优点是因为备选键可以提供更灵活、更高效的查询和更少的主键冲突。缺点是因为使用备选键需要更多的配置和代码,并且可能会影响数据完整性。
以下是一个简单的示例,演示如何使用备选键:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class MyContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 将Email属性指定为备选键
modelBuilder.Entity<Person>().HasAlternateKey(p => p.Email);
}
}
为什么要使用复合备选键?
使用复合备选键的一个重要原因是在某些情况下,单个列可能不足以唯一标识表中的每一行。例如,在一个电影数据库中,如果只使用电影名称作为主键,则会出现多个电影名称相同的情况。因此,需要使用复合备选键,通过多个列来确定每个电影的唯一性。
优点
缺点
使用方式
使用Fluent API是定义复合备选键的最佳方式。以下是使用Fluent API定义复合备选键的示例:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class PersonContext : DbContext
{
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
// 将多个属性配置为备选键(即复合备选键)
.HasAlternateKey(p => new { p.FirstName, p.LastName, p.DateOfBirth })
// 可配置备选键的索引和唯一约束的名称:
.HasName("FirstName_LastName_DateOfBirth");
}
}
可以使用分组配置,这样可以将实体和关系的配置组织在一起,使代码更具可读性。例如:
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder.HasKey(x => x.BlogId);
builder.Property(x => x.Name).IsRequired();
}
}
然后在DbContext中使用以下代码将此配置应用于Blog实体:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new BlogConfiguration());
}
可以使用数据注释来配置EF7模型。数据注释是一种以属性和类注释的方式来提供元数据的方法。例如:
public class Blog
{
[Key]
public int BlogId { get; set; }
[Required]
public string Name { get; set; }
}
在这个示例中,我们使用了Key和Required注释来指定BlogId和Name属性的主键和IsRequired标志。
除了手动配置外,EF7还提供了一些内置约定,可以根据惯例自动推断模型。例如,如果实体中有一个名为Id的属性,EF7会将其作为主键。如果实体之间具有引用关系,EF7会自动创建外键。
如果不想使用内置约定,可以通过调用ModelBuilder.Conventions.Remove方法来删除它们。例如,以下代码删除了为外键列创建索引的约定:
modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
在本文中,介绍了如何告诉EF7使用实体类型和过滤类型,并且还说了共享实体类型。我们还介绍了实体属性的配置。还介绍了四种键。介绍了使用Fluent API和数据注释来配置EF7模型。我们还了解了EF7的内置约定,并学习了如何删除现有约定。使用这些技术,可以轻松地创建和配置EF7模型,并更好地管理数据库访问代码。
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake