当一个实体与另一个实体有多个 0..1:n 关系时,如何使用 Fluent API 定义它们?
When an entity has more than one 0..1:n relationships to another entity, how to define them using Fluent API?
我正在使用 Azure 移动服务,在我的模型中我有一个实体 Message,它在以下语句之后与自身相关:
- A Message 可能有父级 Message,Message 可能是父级许多 消息.
我的class定义如下:
public partial class Message
{
public Message()
{
this.Messages = new HashSet<Message>();
}
public int Id { get; set; }
public int CreatedById { get; set; }
public int RecipientId { get; set; }
public int ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int MessageTypeId { get; set; }
public Nullable<MessageType> Type { get; set; }
public Nullable<bool> Draft { get; set; }
public Nullable<bool> Read { get; set; }
public Nullable<bool> Replied { get; set; }
public Nullable<bool> isDeleted { get; set; }
[JsonIgnore]
public virtual User CreatedBy { get; set; }
[JsonIgnore]
public virtual User Recipient { get; set; }
[JsonIgnore]
public virtual ICollection<Message> Messages { get; set; }
[JsonIgnore]
public virtual Message Parent { get; set; }
}
我的 DTO 是这样的:
public class MessageDTO : EntityData
{
public string CreatedById { get; set; }
public string RecipientId { get; set; }
public string ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string Type { get; set; }
public Nullable<bool> Draft { get; set; }
public Nullable<bool> Read { get; set; }
public Nullable<bool> Replied { get; set; }
public Nullable<bool> isDeleted { get; set; }
}
我使用 AutoMapper 映射了这两个 classes:
/*
* Mapping for Message entity
*/
cfg.CreateMap<Message, MessageDTO>()
.ForMember(messageDTO => messageDTO.Id, map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.Id))))
.ForMember(messageDTO => messageDTO.ParentId, map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.ParentId))))
.ForMember(messageDTO => messageDTO.CreatedById , map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.CreatedById ))))
.ForMember(messageDTO => messageDTO.RecipientId, map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.RecipientId))))
.ForMember(messageDTO => messageDTO.Type, map => map.MapFrom(message => Enum.GetName(typeof(MessageType), message.MessageTypeId)));
cfg.CreateMap<MessageDTO, Message>()
.ForMember(message => message.Id, map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.Id)))
.ForMember(message => message.ParentId, map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.ParentId)))
.ForMember(message => message.CreatedById , map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.CreatedById )))
.ForMember(message => message.RecipientId, map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.RecipientId)));
我的 Fluent API 配置是:
this.Property(m => m.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasRequired(m => m.Parent).WithMany(p => p.Messages).WillCascadeOnDelete(true);
this.HasRequired(m => m.Recipient).WithMany(u => u.MessagesReceived).WillCascadeOnDelete(false);
this.HasRequired(m => m.CreatedBy).WithMany(u => u.MessagesCreated).WillCascadeOnDelete(false);
但是当我尝试插入新消息时出现以下错误:
The operation failed with the following error: 'Entities in
'MobileServiceContext.Messages' participate in the 'Message_CreatedBy'
relationship. 0 related 'Message_CreatedBy_Target' were found. 1
'Message_CreatedBy_Target' is expected.
从这个错误中我了解到我的 Message 期望有一个 Parent,但是因为这将是第一个 Message 没有 Parent,也不是每个 Message 都有父级。在我的 Fluent API 配置中,我根据需要定义了 Parent 属性,因为当我将它设置为可选时,当尝试插入 Message 我收到以下错误:
One or more validation errors were detected during model
generation:FollowAppApi.Models.Message_Parent: : Multiplicity
conflicts with the referential constraint in Role
'Message_Parent_Target' in relationship 'Message_Parent'. Because all
of the properties in the Dependent Role are non-nullable, multiplicity
of the Principal Role must be '1'.
我发送到端点的JSON如下:
{
"createdById": "1",
"recipientId": "2",
"title": "sample string 4",
"content": "sample string 5",
"type": "Alert",
"draft": true,
"read": true,
"replied": true,
"isDeleted": false
}
如何设置我的配置来定义一个可选的 属性 在这种情况下或任何其他应该存在选项 属性 的情况?
编辑 1
所以我检查并 read this 在处理一个实体与另一个实体有两个关系的情况时,我添加了 InverseProperty 注释并得到以下错误:
The operation failed with the following error: 'Unable to determine a
valid ordering for dependent operations. Dependencies may exist due to
foreign key constraints, model requirements, or store-generated
values.
根据我的记忆,这个错误是指存在循环引用,我猜它来自父 属性 或者可能来自与 Message 的用户关系,但我不太确定。
这个错误的原因是什么?
当您有自引用对象时,您需要手动提供外键属性。
this.HasOptional(m => m.Parent)
.WithMany(p => p.Messages)
.HasForeignKey(m => m.ParentId)
.WillCascadeOnDelete(true);
所以出现如下错误:
The operation failed with the following error: 'Unable to determine a
valid ordering for dependent operations. Dependencies may exist due to
foreign key constraints, model requirements, or store-generated
values.
是由于消息中的父 属性 不可为空。请注意,这是在模型 class 上,而不是在 DTO 上,因为字符串已经是可空类型。
所以在消息中我不得不更改:
public int ParentId { get; set; }
为此:
public Nullable<int> ParentId { get; set; }
这让我可以在我的 POST 端点上插入消息。
我正在使用 Azure 移动服务,在我的模型中我有一个实体 Message,它在以下语句之后与自身相关:
- A Message 可能有父级 Message,Message 可能是父级许多 消息.
我的class定义如下:
public partial class Message
{
public Message()
{
this.Messages = new HashSet<Message>();
}
public int Id { get; set; }
public int CreatedById { get; set; }
public int RecipientId { get; set; }
public int ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int MessageTypeId { get; set; }
public Nullable<MessageType> Type { get; set; }
public Nullable<bool> Draft { get; set; }
public Nullable<bool> Read { get; set; }
public Nullable<bool> Replied { get; set; }
public Nullable<bool> isDeleted { get; set; }
[JsonIgnore]
public virtual User CreatedBy { get; set; }
[JsonIgnore]
public virtual User Recipient { get; set; }
[JsonIgnore]
public virtual ICollection<Message> Messages { get; set; }
[JsonIgnore]
public virtual Message Parent { get; set; }
}
我的 DTO 是这样的:
public class MessageDTO : EntityData
{
public string CreatedById { get; set; }
public string RecipientId { get; set; }
public string ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string Type { get; set; }
public Nullable<bool> Draft { get; set; }
public Nullable<bool> Read { get; set; }
public Nullable<bool> Replied { get; set; }
public Nullable<bool> isDeleted { get; set; }
}
我使用 AutoMapper 映射了这两个 classes:
/*
* Mapping for Message entity
*/
cfg.CreateMap<Message, MessageDTO>()
.ForMember(messageDTO => messageDTO.Id, map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.Id))))
.ForMember(messageDTO => messageDTO.ParentId, map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.ParentId))))
.ForMember(messageDTO => messageDTO.CreatedById , map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.CreatedById ))))
.ForMember(messageDTO => messageDTO.RecipientId, map => map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.RecipientId))))
.ForMember(messageDTO => messageDTO.Type, map => map.MapFrom(message => Enum.GetName(typeof(MessageType), message.MessageTypeId)));
cfg.CreateMap<MessageDTO, Message>()
.ForMember(message => message.Id, map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.Id)))
.ForMember(message => message.ParentId, map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.ParentId)))
.ForMember(message => message.CreatedById , map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.CreatedById )))
.ForMember(message => message.RecipientId, map => map.MapFrom(messageDTO => MySqlFuncs.LongParse(messageDTO.RecipientId)));
我的 Fluent API 配置是:
this.Property(m => m.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasRequired(m => m.Parent).WithMany(p => p.Messages).WillCascadeOnDelete(true);
this.HasRequired(m => m.Recipient).WithMany(u => u.MessagesReceived).WillCascadeOnDelete(false);
this.HasRequired(m => m.CreatedBy).WithMany(u => u.MessagesCreated).WillCascadeOnDelete(false);
但是当我尝试插入新消息时出现以下错误:
The operation failed with the following error: 'Entities in 'MobileServiceContext.Messages' participate in the 'Message_CreatedBy' relationship. 0 related 'Message_CreatedBy_Target' were found. 1 'Message_CreatedBy_Target' is expected.
从这个错误中我了解到我的 Message 期望有一个 Parent,但是因为这将是第一个 Message 没有 Parent,也不是每个 Message 都有父级。在我的 Fluent API 配置中,我根据需要定义了 Parent 属性,因为当我将它设置为可选时,当尝试插入 Message 我收到以下错误:
One or more validation errors were detected during model generation:FollowAppApi.Models.Message_Parent: : Multiplicity conflicts with the referential constraint in Role 'Message_Parent_Target' in relationship 'Message_Parent'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
我发送到端点的JSON如下:
{
"createdById": "1",
"recipientId": "2",
"title": "sample string 4",
"content": "sample string 5",
"type": "Alert",
"draft": true,
"read": true,
"replied": true,
"isDeleted": false
}
如何设置我的配置来定义一个可选的 属性 在这种情况下或任何其他应该存在选项 属性 的情况?
编辑 1
所以我检查并 read this 在处理一个实体与另一个实体有两个关系的情况时,我添加了 InverseProperty 注释并得到以下错误:
The operation failed with the following error: 'Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
根据我的记忆,这个错误是指存在循环引用,我猜它来自父 属性 或者可能来自与 Message 的用户关系,但我不太确定。
这个错误的原因是什么?
当您有自引用对象时,您需要手动提供外键属性。
this.HasOptional(m => m.Parent)
.WithMany(p => p.Messages)
.HasForeignKey(m => m.ParentId)
.WillCascadeOnDelete(true);
所以出现如下错误:
The operation failed with the following error: 'Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
是由于消息中的父 属性 不可为空。请注意,这是在模型 class 上,而不是在 DTO 上,因为字符串已经是可空类型。
所以在消息中我不得不更改:
public int ParentId { get; set; }
为此:
public Nullable<int> ParentId { get; set; }
这让我可以在我的 POST 端点上插入消息。