When an entity has more than one 0..1:n relationships to another entity, how to define them using Fluent API?

风流意气都作罢 提交于 2019-12-23 19:08:40

问题


I'm working on an Azure Mobile Service and in my model I have an entity Message, which is related to itself following this statement:

  • A Message may have a parent Message, a Message may be the parent to many Messages.

My class got defined as follows:

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; }
}

And my DTO is this:

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; }

}

I mapped this two classes using AutoMapper like this:

        /*
         * 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)));

And my Fluent API configuration for this is:

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

But when I try to insert a new Message I get the following error:

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.

From this error I'm understanding that my Message is expecting to have a Parent, but since this would be the first Message there's no Parent, also not every Message will have a parent. In my Fluent API configuration I defined the Parent property as required, because when I set it to optional, when trying to insert a Message I got the following error:

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'.

The JSON I'm sending to the endpoint is the following:

{
  "createdById": "1",
  "recipientId": "2",
  "title": "sample string 4",
  "content": "sample string 5",
  "type": "Alert",
  "draft": true,
  "read": true,
  "replied": true,
  "isDeleted": false
}

How can I set my configuration to define an optional property in this scenario or any other where an option property should be present?

EDIT 1

So I checked and read this when dealing with a situation where an entity has two relationships with other entity, I added the InverseProperty annotations and got the following error:

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.

From what I remember, this error refers to a circular reference being present, I guess it's from either the Parent property or maybe either of the User relationships with Message, but I'm not really sure.

What is the cause for this error?


回答1:


So the following error:

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.

Is due to the Parent property in message not being nullable. Take notice this is on the Model class, not the DTO, since string is already a nullable type.

So in Message I had to change:

    public int ParentId { get; set; }

for this:

    public Nullable<int> ParentId { get; set; }

And this allows me to insert Messages on my POST endpoint.




回答2:


When you have self referencing objects, you need to provide the Foreign Key property manually.

this.HasOptional(m => m.Parent) .WithMany(p => p.Messages) .HasForeignKey(m => m.ParentId) .WillCascadeOnDelete(true);



来源:https://stackoverflow.com/questions/28034896/when-an-entity-has-more-than-one-0-1n-relationships-to-another-entity-how-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!