问题
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