I have the following class
public class ObjectA{
private List<ObjectB> list;
}
ObjectA and ObjectB are in N:N relation.
I want to delete only the relation and I use
while (objectA.list.Any())
objectA.list.Remove(objectA.list.First());
List is of the relation table -
List<ObjectAobjectB>
And I get
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.
EDIT: updating model definition
There are three tables in my model :
* ClassA - SchemaA,
* ClassAClassB - SchemaA,
* ClassB - SchemaB,
In my contex (and edmx ) I have only Schema A ( ClassA and ClassAClassB)
There for it is 1:N to the relation Table.
Here is the code generated from the edmx.
public partial class ClassA:DomainEntity
{
....
public virtual ICollection<ClassB> ClassAClassB { get; set; }
}
What am I doing wrong?
Thanks.
If you have one-to-many relation with non-nullable FK you must also delete ObjectB because removing it from navigation property will only remove the relation (makes FK null) but does not remove the ObjectB itself. Try this:
while (objectA.list.Any()) {
var b = b;
objectA.list.Remove(b);
entities.DeleteObject(b);
}
Related
I have a legacy database which I cannot change with two entities with a foreign key property in each entity:
EntityOne
ForeignKey1 (pointing to EntityTwo.ForeignKey2)
EntityTwo
ForeignKey2 (pointing to EntityOne.ForeignKey1)
These foreign keys point to eachother. How can I map this relation with EF6? If it isn't possible, is there any workaround?
I tested with a test database which actually has a foreign key relation from EntityOne to EntityTwo and another from EntityTwo to EntityOne and auto generating the model, with code first from database, did not create any relationships between the two tables:
public partial class EntityFrameworkTesting : DbContext
{
public EntityFrameworkTesting()
: base("name=EntityFrameworkTesting")
{
}
public virtual DbSet<EntityOne> EntityOne { get; set; }
public virtual DbSet<EntityTwo> EntityTwo { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
With some additional trying I've succeeded to build a relationship between these entities. However, I had to delete the foreign key properties from my POCO classes to allow usage of the MapKey function. I would still like to access the foreign key, but when adding the foreign key property again, I receive an error stating: "ForeignKey2: Name: Each property name in a type must be unique. Property name 'ForeignKey2' is already defined.".
Basically I'm looking for the relationship functionality I have now, but with added foreign key property on my POCO class.
modelBuilder.Entity<EntityTwo>()
.HasRequired(entity => entity.EntityOne)
.WithRequiredDependent(entity => entity.EntityTwo)
.Map(m => m.MapKey("ForeignKey2"));
In this situation you want to use a shared primary key, meaning the PK of the dependent entity is also the FK to the principal entity (and references the principal entity's PK), enforcing a 1..0 relationship. You can view this answer to see how it's configured.
If you can't change your database schema, you're out of luck in EF6. EF6 does not support FK's referencing any candidate key other than the primary key.
EFCore supports FK's referencing alternate candidate keys, but still won't create two mutually referencing FK's - only the FK in the dependent table will be created (or so it seems via my limited testing just now). You can do so via the following FluentAPI configuration (assuming EntityOne is the principal entity):
modelBuilder.Entity<EntityOne>()
.HasAlternateKey( eo => eo.ForeignKey1 );
modelBuilder.Entity<EntityTwo>()
.HasOne( et => et.EntityOne )
.WithOne( eo => eo.EntityTwo )
.IsRequired()
.HasForeignKey<EntityTwo>( et => et.ForeignKey2 )
.HasPrincipalKey<EntityOne>( eo => eo.ForeignKey1 );
EntityOne.ForeignKey1 should be nullable. When testing, I had to manually specify the type parameters for the Has*Key methods, not sure why.
I am working with ef6.
I have 2 entities:
House
{
List<Person> Persons
}
Person
{
House House
Nullable<int> HouseId
}
I am trying to delete the house entity without deleting the persons that live in the house this way:
public void DeleteHouse(House house)
{
foreach( var person in house.Persons)
{
person.House = null;
}
house.Persons = null;
context.Houses.Attach(house);
context.Houses.Remove(house)
}
I get:
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.
I just can't seem to figure out how to fix it.
Tx
Suppose the following tables
ParentEntities
ParentID
ChildEntities
ChildID
ParentID
These tables do not have a FK defined in the schema.
In EF designer, after generating from DB, I add an association:
- Parent Multiplicity: 1
- Child Multiplicity: 0 or 1
When I build, I get the error: "Error 3027: No mapping specified for the following EntitySet/AssociationSet - ParentChild"
But if I try to configure table mapping for the association like this..
Maps to ChildEntities
Parent
ParentID <-> ParentID (parent entity prop <-> child table column)
Child
ChildID <-> ChildID (child entity prop <-> child table column)
.. I get this: Error 3007: Problem in mapping fragments starting at lines xxx, xxx: Column(s) [ParentID] are being mapped in both fragments to different conceptual side properties.
Why this is an error doesn't make sense. Limitation of the current implementation?
[EDIT 1]
I'm able to make this work by creating a 1-N association. That's not ideal, but it works just the same, just have to add a read-only child property in a partial:
public partial class Parent
{
public Child Child { get { return Childs.Any() ? null : Childs.First(); } }
}
This seems like the best solution for me. I had to add a FK to the database to get EF to generate the association and navigation property, but once it was added I was able to remove the FK, and further updates to the model from the DB did not remove the association or Navigation properties.
[EDIT 2]
As I was investigating how to work around not caring about the association being modeled in EF, I ran into another issue. Instead of the read-only Child property I made it normal ..
public partial class Parent
{
public Child Child { get; set; }
}
.. but now I need a way to materialize that from the query:
var query = from parents in context.Parents
// pointless join given select
join child in context.Childs
on parents.ParentID equals child.ParentID
select parents;
I can select an anonymous type ..
// step 1
var query = from parents in context.Parents
join child in context.Childs
on parents.ParentID equals child.ParentID
select new { Parent = parents, Child = child };
.. but then I've got to consume more cycles getting that into my entity:
// step 2
var results = query.Select(x => {
var parent = x.Parent;
parent.Child = x.Child;
return parent; });
Is there a better/streamlined way to do this from the query select so the EF materializer can do it from the get-go? If not, then I'll resort to Edit 1 methodology ..
Ef Code first requires 1->0..1 relationships for the Child to have the same primary key.
Maybe this a similar restriction In the modeler in this circumstance.
ParentId (Key) required in Both tables.
I have never tried adding such relationships in designer afterwords in DB first.
EDIT: to match your EDIT2:
I would stay on the direction . Use Navigation properties to get from Join back to original class A and B.
query = context.Set<JoinTable>.Where(Jt=>Jt.NavA.Id == ClassAId
&& Jt.navB.Id == ClassBId)
use a select if your need entries returned from either ClassA or ClassB.
I have an Entity Framework model that has 3 tables (each with indentity primary keys). The root table has a 1 to many relationship to a child table, that child table has a 1 to many relationship with it's child table. This model is reflected correctly in the model that was generated from the database.
In code, we do an Insert (Add) into the parent table, we then do an insert of that children's tables, and then finally we do inserts on the children's children. The code looks similar to the example below:
foreach(var parentItemDTO in someDTOCollection) {
foreach(var someChildDTOItem in someChildDTOCollection) {
// Do some mapping here to the childEntity from DTO
// The foreign key relationship isn't set during mapping.
childTable.Insert(childEntity); // Underlying code is _dbSet.Add(entity)
foreach(var someChildChildDTOItem in someDTOChildChildCollection) {
// Do some mapping here to childChildEntity from DTO
// The foreign key relationship isn't set during mapping.
childChildTable.Insert(childChildEntity); // Underlying code is _dbSet.Add(entity)
}
}
// Do mapping here of the parentEntity from DTO
parentTable.Insert(someEntity); // Underlying code is _dbSet.Add(entity)
}
The inserts into the database seem to be working. However, what I would like to understand is under the hood how does EF maintain the relationship of these objects without me explicitly defining the foreign key relationship during the mapping? Is these inserts scope safe? Will this cause orphans or children being inserted into the wrong parent (right now we don't see this happening but does it have the potential)?
Thanks!
EDITED (CORRECTION):
The code has been updated to reflect that the parent insert is happening AFTER all the children inserts.
For the entities to be tracked properly by EF you need to have properties that represent relationships between entities. You parent entity should have a property referencing children and children in turn should have properties referencing their children. For example:
class ParentEntity {
public int Id { get; set; }
public ICollection<ChildEntity> Children { get; set; }
}
class ChildEntity {
public int Id { get; set; }
}
As long as you add child entities to parent's Children collection EF can keep track of relationships:
var parent = new ParentEntity();
parent.Children.Add(new ChildEntity());
parent.Children.Add(new ChildEntity());
EF knows that object references in parent.Children collection represent new entities (entities not attached to the context) and will handle them accordingly. The actual inserts to the database don't happen until you call SaveChanges(). When you add an object to DbSet EF just begins to keep track of it in memory. Only when you call SaveChanges() entities will be written to the database. At this point EF will figure out that parent entity needs to be saved first. It will then use parent entity's PK as FK in your child entities. Now you can add parent to context and this will also add children:
context.Set<ParentEntity>().Add(parent);
context.SaveChanges(); // adds parent and two children.
I have run into an interesting problem with Entity Framework and based on the code I had to use to tackle it I suspect my solution is less than ideal. I have a 1-to-Many relationship between Table A and Table B where entities in TableB have a reference to TableA. I have a scenario where I want to simultaneously delete all children of a row in TableA and I thought this could be achieve by simply clearing the collection:
Entity.Children.Clear()
Unfortunately, when I attempted to save changes this produced as a Foreign Key violation.
A relationship is being added or
deleted from an AssociationSet
'FK_EntityB_EntityA'. With cardinality
constraints, a corresponding 'EntityB'
must also be added or deleted.
The solution I came up with was to manually delete object via the entity context's DeleteObject(), but I just know this logic I am using has got to be wrong.
while (collection.Any())
Entities.DeleteObject(collection.First());
For one, the fact that I had to use a Where() loop seems far less than ideal, but I suppose that's purely a semantic assessment on my part. In any case, is there something wrong with how I am doing this, or is there perhaps a better way to clear a child entity collection of an entity such that Entity Framework properly calls a data store delete on all of the removed objects?
Clear() removes the reference to the entity, not the entity itself.
If you intend this to be always the same operation, you could handle AssociationChanged:
Entity.Children.AssociationChanged +=
new CollectionChangeEventHandler(EntityChildrenChanged);
Entity.Children.Clear();
private void EntityChildrenChanged(object sender,
CollectionChangeEventArgs e)
{
// Check for a related reference being removed.
if (e.Action == CollectionChangeAction.Remove)
{
Context.DeleteObject(e.Element);
}
}
You can build this in to your entity using a partial class.
You can create Identifying relationship between parent and child entities and EF will delete child entity when you delete it from parent's collection.
public class Parent
{
public int ParentId {get;set;}
public ICollection<Child> Children {get;set;}
}
public class Child
{
public int ChildId {get;set;}
public int ParentId {get;set;}
}
Mapping configuration:
modelBuilder.Entity<Child>().HasKey(x => new { x.ChildId, x.ParentId });
modelBuilder.Entity<Parent>().HasMany(x => x.Children).WithRequired().HasForeignKey(x => x.ParentId);
Trick: When setting up the relationship between Parent and Child, you'll HAVE TO create a "composite" key on the child. This way, when you tell the Parent to delete 1 or all of its children, the related records will actually be deleted from the database.
To configure composite key using Fluent API:
modelBuilder.Entity<Child>().HasKey(t => new { t.ParentId, t.ChildId });
Then, to delete the related children:
var parent = _context.Parents.SingleOrDefault(p => p.ParentId == parentId);
var childToRemove = parent.Children.First(); // Change the logic
parent.Children.Remove(childToRemove);
// you can delete all children if you want
// parent.Children.Clear();
_context.SaveChanges();
Done!