一对零或一对一关系:当IDENTITY_INSERT为OFF时,无法为表中的标识列插入显式值

时间:2018-12-29 13:42:54

标签: c# asp.net entity-framework

我有两个实体:

public class Subscription
{
    public int SubscriptionId { get; set; }

    public virtual ICollection<SubscriptionError> SubscriptionErrors { get; set; }
}

public class SubscriptionError
{
    public int SubscriptionErrorId { get; set; }
    public int SubscriptionId { get; set; }

    public virtual Subscription Subscription { get; set; }
}

最初,我在SubscriptionErrorMap中将它们之间的关系定义为一对多,如下所示:

this.HasRequired(t => t.Subscription)
            .WithMany(t => t.SubscriptionErrors)
            .HasForeignKey(d => d.SubscriptionId)
            .WillCascadeOnDelete(false);

我正在使用以下代码保存SubscriptionError:

context.SubscriptionErrors.Add(subscriptionError);

其中subscriptionError是实体,我没有明确设置主键字段。

这过去工作正常。但是,当我将此关系更改为one to zero-or-one时,它开始在保存时引发以下异常:

  

当IDENTITY_INSERT设置为OFF时,无法在表'SubscriptionError'中为标识列插入显式值。

新的映射是:

this.HasRequired(t => t.Subscription)
            .WithOptional(t => t.SubscriptionError)
            .WillCascadeOnDelete(false);

映射是否有问题?

2 个答案:

答案 0 :(得分:0)

从我看到的数据库模型脚本来看,如下所示

CREATE TABLE Subscription(
    [SubscriptionId] [int] IDENTITY(1,1) NOT NULL,
    --[FieldName1] [nchar](100) NULL,
    --[FieldNameN] [nchar](100) NULL,
 CONSTRAINT [PK_Subscription] PRIMARY KEY CLUSTERED 
(
    [SubscriptionId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE SubscriptionError(
    [SubscriptionErrorId] [int] NOT NULL,
    [SubscriptionId] [int] NULL,
    --[FieldName1] [nchar](100) NULL,
    --[FieldNameN] [nchar](100) NULL,
 CONSTRAINT [PK_SubscriptionError] PRIMARY KEY CLUSTERED 
(
    [SubscriptionErrorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE SubscriptionError  WITH CHECK ADD  CONSTRAINT [FK_SubscriptionError_Subscription] FOREIGN KEY([SubscriptionId])
REFERENCES Subscription ([SubscriptionId]) 
ALTER TABLE [dbo].[SubscriptionError] CHECK CONSTRAINT [FK_SubscriptionError_Subscription] 

哪个[SubscriptionErrorId]是关键,但不是自动递增字段 因此对于one-to-many,关系模型应如下所示:

public partial class Subscription
{ 
    public Subscription()
    {
        SubscriptionErrors = new HashSet<SubscriptionError>();
    } 
    public int SubscriptionId { get; set; } 
    public virtual ICollection<SubscriptionError> SubscriptionErrors { get; set; }
}

SubscriptionError

public partial class SubscriptionError
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int SubscriptionErrorId { get; set; } 
    public int? SubscriptionId { get; set; } 
    public virtual Subscription Subscription { get; set; }
}

这就像一个符咒,但对于one-to-oneone-to-zero关系而言 SubscriptionError的{​​{1}}不是集合,并且该关系应通过流利的api定义,如下所示:

Subscription

和型号:

modelBuilder.Entity<Subscription>()
    .HasOptional(s => s.SubscriptionError)
    .WithRequired(ad => ad.Subscription);

因此,请查看您数据库的public partial class Subscription { public Subscription() { } public int SubscriptionId { get; set; } public virtual SubscriptionError SubscriptionError { get; set; } } public partial class SubscriptionError { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int SubscriptionErrorId { get; set; } public int? SubscriptionId { get; set; } public virtual Subscription Subscription { get; set; } } ,并检查一下ID SubscriptionError(SubscriptionErrorId以ID结尾且EF解释为ID的情况下,EF约定中为SubscriptionError设置了哪些属性找到主键)的int属性,它将将该列配置为标识,这意味着数据库会为该键属性生成值,例如,自动pk将自动映射到标识键,而无需用修饰符明确SubscriptionErrorId,但此处使用DatabaseGeneratedOption,因为该密钥应由代码以实用的方式生成。

关于您的问题,当我们定义DatabaseGeneratedOption.None关系时,这意味着孩子并不意味着如果它的父母不存在,则在这种情况下外键是主键! !对于one-to-one模式的模型来说,SubscriptionError道具也是关键,因为一对一或零关系,如果您查看 here 中的示例,无论如何,在SubscriptionId中没有PK的情况下,如果您没有定义StudentAddress,则会自动生成SubscriptionError SubscriptionErrorId

  

SubscriptionError:EntityType:EntitySet'SubscriptionError'基于没有定义键的'SubscriptionError'类型。

如果您将该列都定义为键,则顺序如下:

PK

您还将获得

  

“ Model.SubscriptionError”中的实体参与“ Subscription_SubscriptionError”关系。找到0个相关的'Subscription_SubscriptionError_Source'。预期为1个“ Subscription_SubscriptionError_Source”。

,但是如果您的模型如下所示,请将[Key, Column("SubscriptionErrorId", Order = 1)] public int SubscriptionErrorId { get; set; } [Key, Column("SubscriptionId", Order = 2)] public int SubscriptionId { get; set; } 定义为SubscriptionErrorId,并将Identity定义为密钥,其中SubscriptionId是自动递增的:

SubscriptionErrorId

然后您的代码将像超级按钮一样工作:

public partial class SubscriptionError
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int SubscriptionErrorId { get; set; } 
    [Key]
    public int SubscriptionId { get; set; } 
    public virtual Subscription Subscription { get; set; }
}

答案 1 :(得分:0)

这对我有用:

删除PK字段SubscriptionErrorId并将SubscriptionId设置为SubscriptionError的PK和FK。

实体现在如下所示:

public class Subscription
{
     public int SubscriptionId { get; set; }

     public virtual SubscriptionError SubscriptionError { get; set; }
}

public class SubscriptionError
{
    [ForeignKey("Subscription")]
    public int SubscriptionId { get; set; }

    [Required]
    public virtual Subscription Subscription { get; set; }
}