Entity delete fails due to FK - entity-framework

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

Related

Entity Framework with MVVM problem. Violation of PRIMARY KEY constraint The duplicate key value is (2, 2)

I've been messing with this for 5 hours and I gave up.
I did basically identical relationship before and worked and now when I'm saving in my MVVM project, I get this error:
SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.LecturerMeeting'. Cannot insert duplicate key in object 'dbo.LecturerMeeting'. The duplicate key value is (2, 2).
And also:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
I have 2 model classes, Meeting and Lecturer. In Lecturer class, I define in constructor:
public Lecturer()
{
Meetings = new Collection<Meeting>();
}
And also
public ICollection<Meeting> Meetings { get; set; }
Same in the Meeting class
public Meeting()
{
Students = new Collection<Student>();
Lecturers= new Collection<Lecturer>();
}
And:
public ICollection<Lecturer> Lecturers { get; set; }
But when I created a many-to-many relationship between meetings and student, EF created a table MeetingStudent.
Now it created LecturerMeeting, don't know if this changes anything
SQL code for LecturerMeeting relationship:
CREATE TABLE [dbo].[LecturerMeeting]
(
[Lecturer_Id] INT NOT NULL,
[Meeting_Id] INT NOT NULL,
CONSTRAINT [PK_dbo.LecturerMeeting] PRIMARY KEY CLUSTERED ([Lecturer_Id] ASC, [Meeting_Id] ASC),
CONSTRAINT [FK_dbo.LecturerMeeting_dbo.Lecturer_Lecturer_Id] FOREIGN KEY ([Lecturer_Id]) REFERENCES [dbo].[Lecturer] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.LecturerMeeting_dbo.Meeting_Meeting_Id] FOREIGN KEY ([Meeting_Id]) REFERENCES [dbo].[Meeting] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_Lecturer_Id]
ON [dbo].[LecturerMeeting]([Lecturer_Id] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_Meeting_Id]
ON [dbo].[LecturerMeeting]([Meeting_Id] ASC);
Please help.
I managed to find answer. Finally.
I didnt incluced another property when i get my Meeting from context.
I had
public async override Task<Meeting> GetByIdAsync(int id)
{
return await Context.Meetings.Include(m => m.Students).SingleAsync(m => m.Id == id);
Insted of:
public async override Task<Meeting> GetByIdAsync(int id)
{
return await Context.Meetings.Include(m => m.Students).Include(m => m.Lecturers)
.SingleAsync(m => m.Id == id);
}
Too stupid to find that faster.

Entity Framework - deleting N:N relation

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

Why is EF cascade delete not working on 0 to 1 relationships?

I have the following table defined through code-first:
public class Action
{
public int ActionId { get; set; }
public int? EmailMessageId { get; set; }
public virtual EmailMessage EmailMessage { get; set; }
}
public class EmailMessage
{
public int EmailMessageId { get; set; }
public string Content { get;set;
}
When an action is created with a corresponding EmailMessage, deleting the action doesn't remove the entry in EmailMessage. It seems EF only creates a cascade delete on 1-many relationships. In this case the relationship is 0 or 1 relationship which has no cascade delete set by default.
I then added the fluent configuration:
modelBuilder
.Entity<Action>()
.HasOptional(x =>x.EmailMessage)
.WithMany()
.HasForeignKey(x=>x.EmailMessageId)
.WillCascadeOnDelete(true);
Which seems to correctly set the CASCADE DELETE when viewing the schema in Management Studio. But when I remove the row from Action manually from the db, the row in EmailMessage remains.
What exactly am I doing wrong here?
I thought I might be getting somewhere when I used 'WithOptionalDependent()' in the configuration. However when I looked at the schema, it had introduced "EmailMessage_EmailMessageId", when I already had EmailMessageId in the table?
Can anyone advise what is wrong here?
The cascade delete is designed to remove the child when the parent is deleted, not the other way around. In this case, the Action is the child associated with the foreign-key of the EmailMessage parent. So deleting the Action will not affect the EmailMessage, but deleting the EmailMessage should cascade delete to the Action.
I have just realized when using "contextName.Entry(parentStudySession).State = System.Data.Entity.EntityState.Deleted;" to delete some parent with a few children..I talk for EF 6.3, EF doesnt't delete the children for us, even didn't delete the parent itself even though we have got cascading on delete constraint. Instead, it gives an error "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 ... "
Instead, when I use "contextName.StudySession.Remove(parentStudySession);" ,
EF 6.3 successfully deletes the PARENT and the CHILDREN... I have cascade on delete constraint.
T.Adakoğlu

One-To-One relationship with fluent api. A Hacky way?

EF 4.3.1. I have defined User and Box entities. Each box may or may not be assigned to a user.
What I'd like to achieve is to have a OwnBox property in User class, and an Owner property in Box class.
in Database, I have defined OwnerId foreignkey in Boxes (Boxes.OwnerId has relation with Users.UserId).
To define the relationship with fluent api, I have defined the following classes:
public partial class User
{
public int UserId {get; set;}
public virtual Box OwnBox { get; set; }
}
public partial class Box
{
public int? OwnerId { get; set; }
public virtual User User { get; set; }
}
Then in my Mapping class for Box, I have defined the relations as follows:
this.HasOptional(t => t.User).WithOptionalDependent(d => d.OwnBox).
Map(m => m.MapKey("OwnerId")).WillCascadeOnDelete(true);
But by firing up the project, I got the error:
Schema specified is not valid. Errors: (56,6) : error 0019: Each
property name in a type must be unique. Property name 'OwnerId' was
already defined.
So I had to tell EF to forget about the OwnerId column first:
this.Ignore(t => t.OwnerId);
Now the project works fine. But I'm still doubtful if this is a good approach and will everything work fine on CRUD operations with foreign key associations.
First of all, this is not one-to-one relationship. In one-to-one relationship the foreign key must be a primary key.
I believe in your scenario the situation can happen:
User = { UserID = 2 }
Box1 = { UserID = 2 }
Box2 = { UserID = 2 }
Nothing stops you from doing that, but which box should be returned when you do that:
User.OwnBox, Box1 or Box2?
EF can deal with that using Independent Association. It will create foreign key, hidden from your POCO class. You can specify the name of the column using MapKey as you did. However, because you also created a property called OnwerID, just as the column used with MapKey, the EF has a problem as two properties are mapped to the same column.
When you use ignore, the POCO OwnerID property is ignored by EF so that fixes the problem of two properties, however, the OwnderID value never gets saved or read to the database. Because EF just ignores it.
Thanks for your question, I have learnt a lot thanks to this.

EF delete entity with unloaded navigation property

I have an Entity. Mandate. Every mandate has a required:many relation to a Person (NavigationProperty). I use the DbContext API with (LazyLoadingEnabled, AutoDetectChangesEnabled, ValidateOnSaveEnabled, ProxyCreationEnabled)
Now I like to delete a Mandate entity. The mandate entities are loaded by another context with AsNoTracking().
message.Result.
ObserveOn(On<DataComposition>.Scheduler).
Where(r => r).
Subscribe(_ =>
{
using (var unit = UnitOfWork.Begin())
{
var mandate = this.SelectedItem.OriginalEntity;
this.mandateRepository.Attach(mandate);
// mandate.Person.ToString();
this.mandateRepository.Delete(mandate);
unit.Commit();
}
this.List.RemoveOnUi(this.SelectedItem);
});
Now during committing I get the following exception: Entities in 'CodeFirstContainer.Mandates' participate in the 'Mandate_Person' relationship. 0 related 'Mandate_Person_Target' were found. 1 'Mandate_Person_Target' is expected.
The delete works if I include the Person Property during the population/selection or if I visit the Property (lazyloading), but I DONT LIKE to materialize/hold many entities only for the deletion case and I DONT LIKE to trigger more than a single DELETE query to db!
The fact that, if you have the navigation property mandate.Person populated, the following SQL statement ...
delete [dbo].[Mandates]
where (([Id] = #0) and ([PersonId] = #1))
... is sent to the database, lets me think that the navigation property indeed must be populated with a person with the correct PersonId to delete the parent.
I have no idea why Entity Framework just doesn't send a delete statement with the primary key ...
delete [dbo].[Mandates]
where ([Id] = #0)
... as I had expected.
Edit
If the Mandate entity has a foreign key property PersonId for the Person navigation property, the expected SQL (the second above) is sent to the database. In this case the Person navigation property can be null and the value of the FK property PersonId doesn't matter.
Edit 2
If you don't want to introduce a FK property the way with the least DB-roundtrip-costs would probably be to fetch the person's Id and then create a dummy person with that key in memory:
// ...
var personId = context.Mandates
.Where(m => m.Id == mandate.Id)
.Select(m => m.Person.Id)
.Single();
mandate.Person = new Person { Id = personId };
this.mandateRepository.Attach(mandate);
this.mandateRepository.Delete(mandate);
// ...