EF issue adding entity with list of child entities - c#

I've been trying to update an entity that will have a list of child entities but for some reason I keep getting the same error and I don't know what I am doing wrong. Any help will be appreciated. The error is:
The operation failed: The relationship could not be changed because
one or more of the foreign-key properties is non-nullable. When a
change is made to a relationship, the related foreign-key property is
set to a null value. If the foreign-key does not support null values,
a new relationship must be defined, the foreign-key property must be
assigned another non-null value, or the unrelated object must be
deleted.
Bellow is what I am trying:
public class Parent
{
[Key]
public int Id { get; set; }
[MaxLength(25)]
public string Name { get; set; }
[MaxLength(25)]
public string FullName { get; set; }
public virtual ICollection<Child> ChildList { get; set; } = new HashSet<Child>();
}
public class Child
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(12)]
public string Number { get; set; }
[IgnoreMap]
public Parent Parent { get; set; }
public int ParentId { get; set; }
}
public async Task<Parent> UpdateParent(Parent Parent)
{
Parent originalParent = await GetById(Parent.Id, c => c.ChildList);
Mapper.Map(Parent, originalParent);
await DbContext.SaveChangesAsync();
return originalParent;
}
Also I have noticed that if I don't use automapper and I just map the properties manually it works:
private static void MapParentProperties(Parent parent, Parent originalParent)
{
originalParent.Name = parent.Name;
originalParent.FullName = parent.FullName;
}

I haven't used Automapper with EF for a while due to past issues similar to this and lazy load trigger, but I've been digging around into it lately given it seems to have better IQueryable support. I'm not sure if it's still the case, but Automapper had a limitation that mapped child collections would be replaced by default, and with EF this could be treated as new entities being marked for insertion or existing child entities getting "bumped out" of the parent and EF flagging that their FK could not be #nulled. The solution was to tell Automapper to keep the destination collection, then handle those mappings individually. (using the child maps) There might be some additional work needed to handle inserts & deletes in the modified collection.
Something that may help:
http://bzbetty.blogspot.com/2012/06/updating-collections-using-automapper.html

Related

relationship can't be changed , foreign-keys are non-nullable

I'm running an (old) MVC3 web app on an MSSQL DB, utilizing EF 6.0 code first and the repository pattern.
the system has been running in production for the last 7 years (EF was updated about 1 year ago).
I've been encountering a very strange exception in 1 particular area of the system.
when attempting to create or update certain entities, I encounter the following exception:
The operation failed: The relationship could not be changed because
one or more of the foreign-key properties is non-nullable. When a
change is made to a relationship, the related foreign-key property is
set to a null value. If the foreign-key does not support null values,
a new relationship must be defined, the foreign-key property must be
assigned another non-null value, or the unrelated object must be
deleted.
here's one of the problematic entities:
public class BeaconAppErrorLog
{
[Key]
public int Id { get; set; }
public int EntityId { get; set; }
public string RawJson { get; set; }
public DateTime SavedAt { get; set; }
public int? EmployeeId { get; set; }
[ForeignKey("EmployeeId")]
public Employee Employee { get; set; }
public int? ContainerId { get; set; }
[ForeignKey("ContainerId")]
public Container Container { get; set; }
public int? DailyTrackId { get; set; }
[ForeignKey("DailyTrackId")]
public DailyTrack DailyTrack { get; set; }
public int? ClientId { get; set; }
[ForeignKey("ClientId")]
public Client Client { get; set; }
public string Error { get; set; }
}
here's the code for creation & saving :
DataContext.BeaconAppErrorLogs.Add(new BeaconAppErrorLog()
{
EntityId = 2,
SavedAt = DateTime.Now,
EmployeeId = activity.EmployeeId,
DailyTrackId = activity.DailytrackId,
Error = error
});
DataContext.SaveChanges();
the 'EmployeeId' and 'DailyTrackId' fields are foreign keys, they receive
valid values (i.e id's that correspond to the respective entity and exist in the DB)
an almost identical code is written hundreds of time throughout the application - and is functioning properly (even for the exact same entity).
I have no idea what is going on and why, and so far all of the solutions I've attempted did not work.
Your help will be greatly appreciated!
Thanks,
Nir
turns out the solution was someplace else entirely.
as #gregH and #tschmit007 have pointed out, EF tracks changes in entities.
once we've started moving the 'DataContext.SaveChanges()' command higher up the execution chain we've found that changes being made to one of the entities higher up were causing the issue.
the actual problem was a modification of a child collection property to the DailyTrack entity. the modification consisted of filtering the collection data (a LINQ WHERE Claus performed on the collection).
thanks, everyone for your help.

Duplicate entities when setting state within a collection in Entity Framework

I have a solution which uses Entity Framework to insert invoices to a database table. These invoices reference an order, which in turn also references an order item collection.
In this instance I am trying to add an order to the database, however the code is inside a new DbContext and so I need to attach the order and order items to the context, as these already exist in the database and shouldn't be re-added.
I've cut down the model properties for the sake of demonstration:
public class Invoice {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int InvoiceId { get; set; }
public string OrderNumber { get; set; }
...
public virtual List<InvoiceLineItem> LineItems { get; set; }
}
public class InvoiceLineItem {
[Key]
public int Id { get; set; }
...
public ShopifyOrderItem { get; set; }
}
public class ShopifyOrder {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public int OrderNumber { get; set; }
...
public OrderInvoiceStatus InvoiceStatus { get; set; }
public virtual List<ShopifyOrderItem> OrderItems { get; set; }
}
public class ShopifyOrderItem {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
...
[Required]
public virtual ShopifyOrder ShopifyOrder { get; set; }
}
In the invoice engine, I'm running the following code for each invoice to add it to the database:
ShopifyOrder order = await db.ShopifyOrders.SingleOrDefaultAsync(x => x.OrderNumber.ToString() == inv.OrderNumber);
if (order != null) {
// Attach marketplace entity to the invoice to avoid duplicate primary key exceptions
db.Marketplaces.Attach(inv.Marketplace);
db.Invoices.Add(inv);
order.InvoiceStatus = OrderInvoiceStatus.InProgress;
}
I've tried a number of methods to try and attach the states, however they all throw errors.
inv.LineItems.ForEach(li => {
db.Entry(li).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem.ShopifyOrder).State = EntityState.Modified;
});
The above code returns the following error on save:
EntityFramework: Saving or accepting changes failed because more than one entity of type 'TorroModels.ShopifyOrder' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model.
What is the best way to attach the LineItems/ShopifyOrderItems without trying to attach the ShopifyOrder connected property multiple times?
Sorry to say but it seems that you need to follow the best practice first when constructing a relationship. You may follow this link :
http://www.entityframeworktutorial.net/entity-relationships.aspx
In short :
Avoid using only "Id" in every entity, or you can use attributes to map between the physical name and the property name
It seems that you have circular references here, so maybe you could simplify it first
Next, you can read this link :
http://www.entityframeworktutorial.net/EntityFramework5/attach-disconnected-entity-graph.aspx
if you need to know more about what's the best practice of attaching entities, but in my opinion, just don't abuse this feature, because using normal CRUD should be sufficient most of the time.
I'm sorry I cannot help you more than this, because of lack of information I may need, and with my reputation I still cannot comment directly in your post to ask for it.

EF - One to one relationships: ObjectContext instance has been disposed

I am having some trouble with one to one relationships in EF.
Normally, I would define the key side of a (one to many) relationship, like so:
[ForeignKey("OrderId")]
public virtual Order Order { get; set; }
But when I do this with a one to one relationship, it fails to determine the principal end...
Unable to determine the principal end of an association between the
types 'Retailer.ClientDB.EF.OrderOrigin' and
'Retailer.ClientDB.EF.Order'. The principal end of this
association must be explicitly configured using either the
relationship fluent API or data annotations.
Strangely, I don't get the error if I put the ForeignKeyAttribute on the id column, as I have done here with the relationship between Order and OrderOrigin.
[Table("ORDER_ORIGIN")]
public class OrderOrigin
{
[Key, Column("uidorder"), ForeignKey("Order")]
public int OrderId { get; set; }
[Column("importfilename")]
public string ImportFileName { get; set; }
public virtual Order Order { get; set; }
}
[Table("ORDERS")]
public class Order
{
[Column("uidorder")]
public int Id { get; set; }
[Column("uidstatus")]
public int OrderStatus { get; set; }
public virtual OrderOrigin OrderOrigin { get; set; }
}
But now, when I attempt to include() like this (which would normally work great)...
public List<ClientDB.EF.OrderOrigin> ListOrderOrigin_WithOrder(Common.Data.DBContext context)
{
return context.EFDatabase.OrderOrigin.Include("Order").ToList();
}
...it fails to include Order and errors with the following when I try and access Order (after the context has been disposed of):
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
Can anyone please advise how I can code one to one relationships in order to get this to work?
Thanks

Fluent NHibernate Relation Without Foreign Key

I'm trying so simplify my problem here, but basically I'm trying to map 2 entities however i don't have a Foreign Key in the database set, since the column could be null. When I try to do an insert on the parent, I'm getting the following error:
object references an unsaved transient instance - save the transient
instance before flushing or set cascade action for the property to
something that would make it autosave.
This is what I have so far:
My entities
public class DocumentDraft
{
public virtual Guid Id { get; set; }
public virtual string Subject { get; set; }
public virtual string ReferenceNo { get; set;}
public virtual DocumentType DocumentType { get; set; }
}
public class DocumentType
{
public virtual short Id { get; set; }
public virtual string Description { get; set; }
}
Mapping
public class DocumentDraftMap : ClassMap<DocumentDraft>
{
public DocumentDraft()
{
// other mappings ...
References(x => x.DocumentType)
.Columns("DocumentTypeId")
.Nullable()
.Not.LazyLoad()
.NotFound.Ignore(); // <-- added this since the value could be null and it throws an error
}
}
I tried specifying Cascade.None() in the mapping, but I'm getting the same result. Basically what happens is that a null value is attempted at being inserted in the DocumentType, and I don't want this (I want to insert null in the parent table, but I don't want to touch the child tables at all, I don't want this to cascade).
I've also tried: .Not.Insert(), but that didn't work either.
I'd appreciate it if someone could help me out on this one.
I guess the property DocumentType is not really null when saving.
It seems there is an instance and without Cascade.All() on the reference it can not be saved.

Many-to-many relationships using EF Code First

I have two classes defined as such:
public class Questionnaire
{
public int QuestionnaireID { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Vendor> Vendors { get; set; }
}
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
public virtual ICollection<Questionnaire> OpenQuestionnaires { get; set; }
public virtual ICollection<Questionnaire> SubmittedQuestionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
I beleive this is the correct way to establish a many-to-many relationship between these classes, and when the project is built, I would expect three tables to be created.
However, when I attempt to to relate one Questionnaire to two different Vendors, I receive the following error when attempting to save the changes (context.SaveChanges()):
*Multiplicity constraint violated. The role 'Vendor_OpenQuestionnaires_Source' of the relationship 'QuestionnaireApp.Models.Vendor_OpenQuestionnaires' has multiplicity 1 or 0..1.*
If I assign a Questionnaire to only one Vendor, save the changes and then assign it to another and again save changes I no longer get the error; however the Questionaire is then related only to the last Vendor to which it was assigned, indicating that (at best) there is a one-to-many relationship being created.
I'm hoping that there is something wrong with the way I'm declaring the many-to-many relationship between these classes, or perhaps there is something I need to add to the context class to "encourage" the relationsip, but perhaps many-to-many relationships like this are not supported, or cannot be created using "Code First"?
Thank you for your time,
Jason
If you don't have any Fluent API code your expected mapping relies on EF Code First conventions. The convention which you expect to kick in here is the AssociationInverseDiscoveryConvention. Now if you look in Intellisense (and probably also documentation) it says about this convention:
Convention to detect navigation properties to be inverses of each
other when only one pair of navigation properties exists between the
related types.
Now, that's the problem: You don't have only "one pair" of navigation properties between Questionnaire and Vendor. You have two collections in Vendor refering to Questionnaire and one collection in Questionnaire refering to Vendor. The result is that this convention doesn't get applied and EF maps actually three one-to-many relationships with only one end exposed as navigation property in the model.
Moreover the mapping you want to achieve is not possible with your model: You cannot map the one end Questionnaire.Vendors to the two ends Vendor.OpenQuestionnaires and Vendor.SubmittedQuestionnaires.
One workaround is to change your model the following way:
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
[NotMapped]
public IEnumerable<Questionnaire> OpenQuestionnaires
{
get { return Questionnaires.Where(q => q.IsActive); }
}
[NotMapped]
public IEnumerable<Questionnaire> SubmittedQuestionnaires
{
get { return Questionnaires.Where(q => !q.IsActive); }
}
public virtual ICollection<Questionnaire> Questionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
Now Vendor.Questionnaires is mapped to Questionnaire.Vendors (AssociationInverseDiscoveryConvention should detect this) and the helper properties OpenQuestionnaires and SubmittedQuestionnaires allow you to pull out the selected items. (I'm not sure if IsActive is your distinguishing flag. Otherwise you have to introduce some new flag.)
The [NotMapped] attribute is just here to make it explicite. It is probably not necessary because EF won't map IEnumerable collections and readonly properties with only a getter anyway.
Go figure, after an hour or so of searching, I go and find the exact answer 30 seconds after I post my question.
The solution was to add the following to the context class:
modelBuilder.Entity<Vendor>()
.HasMany<Questionnaire>(x => x.OpenQuestionnaires)
.WithMany(x => x.Vendors)
.Map(x =>
{
x.MapLeftKey("vID");
x.MapRightKey("qID");
x.ToTable("VendorQuestionnaires");
});
I found the answer by reading this Stack Overflow post: EF Code First Many-to-Many not working

Categories

Resources