C# 我需要帮助解决实体框架为何获胜';如果将虚拟关键字添加到属性,则无法保存

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属性表示。我承认我从我的另一个项目中复制了数据上下文,并对其进行

我创建了一个名为Random Data Generator的开源项目:

我还发布了一段6分钟的短片,让你看看这是否值得克隆:

Random Data Generator是一个示例项目,它演示了我的开源项目DataTier.Net与Entity Framework相比要快多少,因为DataTier.Net使用了所有存储过程

这个项目已经完成并正在运行,但在我开始吹嘘DataTier.Net()比EF快x倍之前,我想确保我给出了EF属性表示。我承认我从我的另一个项目中复制了数据上下文,并对其进行了修改,但可能遗漏了一些内容

如果任何超级英雄(EF-Man?)实体框架爱好者有几分钟的时间来看一看,这可能会保留模糊的答案,因为代码和数据库已经发布

我想这是因为我累了,但是如果我将virtual关键字添加到Address属性,Entity Framework将停止保存成员对象,因此我必须通过一次来保存成员,然后通过另一次来保存地址

(停止保存任何内容)

我已经建立了两个表之间的关系(我认为):

(这可能更容易 使用[随机数据] 去

在[主]上允许行锁定,允许页锁定 )在[小学] 去


感谢您的帮助。

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();