当一个实体与另一个实体有多个 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,它在以下语句之后与自身相关:

我的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 端点上插入消息。