C# 我需要帮助解决实体框架为何获胜';如果将虚拟关键字添加到属性,则无法保存
我创建了一个名为Random Data Generator的开源项目: 我还发布了一段6分钟的短片,让你看看这是否值得克隆: Random Data Generator是一个示例项目,它演示了我的开源项目DataTier.Net与Entity Framework相比要快多少,因为DataTier.Net使用了所有存储过程 这个项目已经完成并正在运行,但在我开始吹嘘DataTier.Net()比EF快x倍之前,我想确保我给出了EF属性表示。我承认我从我的另一个项目中复制了数据上下文,并对其进行了修改,但可能遗漏了一些内容 如果任何超级英雄(EF-Man?)实体框架爱好者有几分钟的时间来看一看,这可能会保留模糊的答案,因为代码和数据库已经发布 我想这是因为我累了,但是如果我将virtual关键字添加到Address属性,Entity Framework将停止保存成员对象,因此我必须通过一次来保存成员,然后通过另一次来保存地址 (停止保存任何内容) 我已经建立了两个表之间的关系(我认为): (这可能更容易 使用[随机数据] 去 在[主]上允许行锁定,允许页锁定 )在[小学] 去C# 我需要帮助解决实体框架为何获胜';如果将虚拟关键字添加到属性,则无法保存,c#,entity-framework,C#,Entity Framework,我创建了一个名为Random Data Generator的开源项目: 我还发布了一段6分钟的短片,让你看看这是否值得克隆: Random Data Generator是一个示例项目,它演示了我的开源项目DataTier.Net与Entity Framework相比要快多少,因为DataTier.Net使用了所有存储过程 这个项目已经完成并正在运行,但在我开始吹嘘DataTier.Net()比EF快x倍之前,我想确保我给出了EF属性表示。我承认我从我的另一个项目中复制了数据上下文,并对其进行
感谢您的帮助。
virtual
与此问题无关(它控制与此无关的延迟加载行为)。示例EF模型与示例数据库模型不匹配。在使用现有数据库时,实体模型的正确映射对于EF的正常运行至关重要
因为所有EF运行时行为(查询生成、插入/更新/删除操作及其执行顺序等)都基于根据约定、数据注释和流畅配置构建的实体模型,而不是实际的数据库
让我们看看您当前的实体模型(删除所有不必要的显式字段/属性):
如您所见,只有创建带有列的表,但没有FK约束
因此,第一步是删除[NotMapped]
属性。现在真正的问题来了。数据库中对成员ID
列没有唯一约束的FK关系意味着一对多关系,即一个成员可以有多个地址
,因此导航属性不能是单个地址
,而是集合:
public partial class Member
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Address> Addresses { get; set; }
}
现在,模型与数据库匹配,您可以创建成员以及一个或多个关联的地址。但这需要更改您的代码,该代码似乎假定成员具有零或一个地址,即一对一关系
如果一对一是预期的关系,那么数据库设计就不合适。对于这种场景(当然也得到了EF的支持)最好的是所谓的,其中,地址的PK也用作FK,而不是单独的FK成员ID
这将是EF的最佳设计。不幸的是,这需要更改数据库设计,因此不能与用于比较的公共数据库一起使用
有一种方法可以保持数据库设计的原样,并创建所需的一对一映射(尽管从技术上讲,并没有在数据库中强制)。这需要将成员的地址
属性映射为参考导航属性,从地址
类中删除显式FK属性成员ID
(EF6限制),并使用fluent API将FK列映射为阴影属性:
public partial class Member
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//[NotMapped] <-- remove this
public Address Address { get; set; }
}
public partial class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string City { get; set; }
//public int MemberId { get; set; } <-- remove this
public int StateId { get; set; }
public string StreetAddress { get; set; }
public string ZipCode { get; set; }
public string Unit { get; set; }
[NotMapped]
public bool IsNew => this.Id < 1;
}
现在,这样的代码将创建成员
和关联的地址
:
var member = new Member
{
FirstName = "FN1",
LastName = "LN1",
Address = new Address
{
City = "C1",
Unit = "U1",
ZipCode = "ZC1",
StreetAddress = "SA1",
}
};
dbContext.Set<Member>().Add(member);
dbContext.SaveChanges();
var成员=新成员
{
FirstName=“FN1”,
LastName=“LN1”,
地址=新地址
{
City=“C1”,
Unit=“U1”,
ZipCode=“ZC1”,
StreetAddress=“SA1”,
}
};
dbContext.Set().Add(成员);
dbContext.SaveChanges();
重述:
EF对如何定义关系及其基数更为严格,因为许多运行时行为都依赖于此。为了获得正确的操作处理,必须正确映射
另外,作为旁注,CUD性能绝对不是EF的优势之一,因此将其与另一个库IMHO进行比较没有多大意义。它还有许多其他的优点,如果需要的话,CUD操作的性能可以通过一些第三方的批量插入/更新/删除扩展来显著提高。这是一个非常好的答案,我觉得我已经学会了EF,尽管我对它的概念一无所知。谢谢你的回答。我将添加关系。我添加了“未映射”,因为它没有保存,但您的回答解释了原因。让我代表EF就像南希·佩洛西代表共和党一样。这就是我寻求帮助的原因。非常感谢。哈哈,很高兴能帮上忙:)如果表已经存在,我理解createtable的用法。我没有首先使用代码,所以仍然需要这样做吗?谢谢,我可以更改我的数据库。我只是为一个演示项目做的。
public partial class Member
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
public Address Address { get; set; }
}
public partial class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string City { get; set; }
public int MemberId { get; set; }
public int StateId { get; set; }
public string StreetAddress { get; set; }
public string ZipCode { get; set; }
public string Unit { get; set; }
[NotMapped]
public bool IsNew => this.Id < 1;
}
CreateTable(
"dbo.Address",
c => new
{
Id = c.Int(nullable: false, identity: true),
City = c.String(),
MemberId = c.Int(nullable: false),
StateId = c.Int(nullable: false),
StreetAddress = c.String(),
ZipCode = c.String(),
Unit = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Member",
c => new
{
Id = c.Int(nullable: false, identity: true),
Active = c.Boolean(nullable: false),
FirstName = c.String(),
LastName = c.String(),
})
.PrimaryKey(t => t.Id);
public partial class Member
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Address> Addresses { get; set; }
}
CreateTable(
"dbo.Address",
c => new
{
Id = c.Int(nullable: false, identity: true),
City = c.String(),
MemberId = c.Int(nullable: false),
StateId = c.Int(nullable: false),
StreetAddress = c.String(),
ZipCode = c.String(),
Unit = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Member", t => t.MemberId, cascadeDelete: true)
.Index(t => t.MemberId);
CreateTable(
"dbo.Member",
c => new
{
Id = c.Int(nullable: false, identity: true),
Active = c.Boolean(nullable: false),
FirstName = c.String(),
LastName = c.String(),
})
.PrimaryKey(t => t.Id);
public partial class Member
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//[NotMapped] <-- remove this
public Address Address { get; set; }
}
public partial class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string City { get; set; }
//public int MemberId { get; set; } <-- remove this
public int StateId { get; set; }
public string StreetAddress { get; set; }
public string ZipCode { get; set; }
public string Unit { get; set; }
[NotMapped]
public bool IsNew => this.Id < 1;
}
modelBuilder.Entity<Member>()
.HasOptional(e => e.Address)
.WithRequired()
.Map(m => m.MapKey("MemberId"))
.WillCascadeOnDelete(true);
var member = new Member
{
FirstName = "FN1",
LastName = "LN1",
Address = new Address
{
City = "C1",
Unit = "U1",
ZipCode = "ZC1",
StreetAddress = "SA1",
}
};
dbContext.Set<Member>().Add(member);
dbContext.SaveChanges();