我有两个实体:
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);
映射是否有问题?
答案 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-one
或one-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; }
}