I have an entity model with User and Person entities, such that each User must be associated with exactly 1 Person, and each Person can be associated zero or 1 User.
User (0..1) <-------> (1) Person
The association is mapped fluently. Originally I only had it declared on the Person side:
private class PersonOrm : EntityTypeConfiguration<Person>
{
internal PersonOrm()
{
ToTable(typeof(Person).Name, DbSchemaName.People);
// has zero or one User
HasOptional(p => p.User)
.WithRequired(d => d.Person)
.Map(d => d.MapKey("PersonId"))
.WillCascadeOnDelete(false)
;
Since I encountered this error, I also added the same mapping to the User side:
private class UserOrm : EntityTypeConfiguration<User>
{
internal UserOrm()
{
ToTable(typeof(User).Name, DbSchemaName.Identity);
// has exactly 1 Person
HasRequired(p => p.Person)
.WithOptional(d => d.User)
.Map(d => d.MapKey("PersonId"))
.WillCascadeOnDelete(false);
There is a scenario in the application where a new User can be created and associated with an existing Person. This is where I am having difficulty at the moment. EF is considering User as the dependent side of the relationship, and is putting a PersonId (FK, int, not null) column on the User table. I don't believe it's possible to use a foreign key property on either entity to help EF manage the association (is it?).
Here is some failing code that tries to handle the scenario:
// find out if Person already exists
var person = context.People.SingleOrDefault(p => p.Emails.Any(
e => e.Value.Equals(emailAddress, StringComparison.OrdinalIgnoreCase)));
// find out if User already exists
var user = context.Users.SingleOrDefault(
u => u.Name.Equals(emailAddress, StringComparison.OrdinalIgnoreCase));
if (user == null)
{
user = new User
{
Name = emailAddress,
IsRegistered = isRegistered,
Person = person ?? PersonFactory.Create(emailAddress),
// ignore the PersonFactory.Create, that part works
};
context.Entry(user).State = EntityState.Added;
context.SaveChanges();
}
This code works fine when person is null (does not already exist in the db). However when person is not null (already exists in db) and user is null, the code attempts to create a new User and associate it with the existing Person. When invoking SaveChanges(), I get a DbUpdateException:
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.
The inner exception is:
A relationship from the 'User_Person' AssociationSet is in the
'Deleted' state. Given multiplicity constraints, a corresponding
'User_Person_Source' must also in the 'Deleted' state.
This doesn't make any sense to me because I'm not trying to delete anything, and checking the EntityState of both User and Person shows that User is in the Added state, whereas Person is in the Unchanged state. I have overridden SaveChanges() to demonstrate:
public override int SaveChanges()
{
var userChanges = ChangeTracker.Entries<User>().Single();
var personChanges = ChangeTracker.Entries<Person>().Single();
if (userChanges.State == EntityState.Deleted)
throw new InvalidOperationException("wtf?");
if (personChanges.State == EntityState.Deleted)
throw new InvalidOperationException("wtf?");
return base.SaveChanges();
}
When this code executes, neither InvalidOperationException is thrown. Again, userChanges is in the Added state, and personChanges is in the Unchanged state.
What am I doing wrong?
I feel really dumb right now. After writing up this careful question, it's now obvious.
The Person I am testing with already exists, and already has a User association with a different User.Name. This is why user is coming up null. Setting the Person.User property to a new User is causing the old User to be put in the Deleted state. Doh.
Sorry to have wasted your time. I'll leave the question up unless it's agreed it would be better to delete it.
Make sure you have the relationship defined in your mapping (Entity Type Configuration)
For example:
this.HasRequired(t => t.QuoteResponse).WithMany(t => t.QuoteResponseItems).HasForeignKey(d => d.QuoteResponseID);
Related
I have an issue with a code first app.
When I tried to insert test item in my database I have an already exist error on child issue.
here the code of my newitem Operation :
public async Task<ActionResult<Event>> NewEvent(Event newEvent)
{
if (await _context.Events.CountAsync() > 0 && await _context.Events.FindAsync(newEvent.Id) is not null)
return BadRequest(new ConstraintException("Event Already Exist"));
if (newEvent.DoorPrize is not null && newEvent.DoorPrize.Count() > 0)
{
var doorPrizes = newEvent.DoorPrize.Where(d => _context.DoorPrizes.Contains(d)).ToList();
foreach (DoorPrize doorPrize in doorPrizes)
{
_context.Entry(doorPrize).State = EntityState.Detached;
}
foreach (DoorPrize doorPrize in newEvent.DoorPrize)
{
if (_context.FairlightUsers.Contains(doorPrize.Sponsor))
_context.Entry(doorPrize.Sponsor).State = EntityState.Detached;
}
}
if (newEvent.AttendeeDetails is not null && newEvent.AttendeeDetails.Count() > 0)
{
var attendeeDetails = _context.EventAttendeeDetails.Where(d => newEvent.AttendeeDetails.Contains(d)).ToList();
foreach (EventAttendeeDetail attendeeDetail in attendeeDetails)
{
_context.Entry(attendeeDetail).State = EntityState.Detached;
}
}
if (newEvent.VenueAddress is not null)
{
if (_context.Addresses.Contains(newEvent.VenueAddress))
_context.Entry(newEvent.VenueAddress).State = EntityState.Detached;
}
if (newEvent.Sponsor is not null)
{
if (_context.FairlightUsers.Contains(newEvent.Sponsor))
_context.Entry(newEvent.Sponsor).State = EntityState.Detached;
}```
I don't know why, even if I make Sponsors (the 2) to detached, the application still try to add one.
Is someone see where is my mistake ?
My goal would be to avoid any child insertion because app need to take it from existing list but I don't success to achieve this. the application always try to create children event with setting them as detached. is there a method to avoid this ?
Working with detached entities are a nuisance. Working with detached entity graphs are a complete pain. There are several issues with your approach, namely when working with detached entities, the database state should always be treated as the point of truth for which you apply changes from your detached state only after you validate that they are still relevant. (I.e. someone else hasn't modified the entity data since your current copy had been taken.
First off,
await _context.Events.CountAsync() > 0
&& await _context.Events.FindAsync(newEvent.Id) is not null
is completely unnecessary. Why would you tell the DbContext to execute a count AND load the entity just to determine if the entity exists? If you want to know if an entity exists:
var doesExist = _context.Events.Any(x => x.Id == newEvent.Id);
if (doesExist)
return BadRequest(new ConstraintException("Event already exists"));
This executes an IF EXISTS SELECT against the database which is much faster.
Not every operation on the DbContext needs to be async. Asynchronous calls are useful for operations that will take some time to run. They come with an small extra performance cost so any operation that can be done quickly such as fetching individual entities or reasonable entity graphs can just be done synchronously.
Next, when dealing with detached entities you generally do not want to detach existing entities, and especially not references, to be overwritten by the detached entities coming in. In short you should never trust data coming into the domain to be current or safe from unexpected tampering, either by bugs or malicious consumers. For example if you have a web application where the server sends an detached entity to be rendered, then the client presents fields to be changed then the Form or Ajax POST (Javascript) serializes data into an Entity class to send back to the server, it is very easy to miss values resulting in #nulls, and malicious users can use browser debugging tools to intercept the POST, view the entity data and make changes which code like the above could unwittingly overwrite data. What gets passed in may look like an entity, but it is often not.
Instead, without changing the fact that a detached entity graph is being passed in, treat it like a DTO. The data in Event will serve as a new entity, but everything related to it you will need to decide whether those represent new entities or references to existing ones. So for instance if the relationship between an Event and a DoorPrize is one to many, where a DoorPrize entity would be created with the new event, and only ever associated with that entity, then it stands that it should be allowed to be inserted with that entity. If instead the DoorPrize is its own entity and merely associated with this Event (and others) then it needs to be re-associated with the data state.
The difference between the two: 1-to-many (Event owns DoorPrizes) in the database would have an EventId on the DoorPrize table. Many-to-many (Event is associated with DoorPrizes) There would be an EventDoorPrize linking table containing the EventId & DoorPrizeId.
In the first case, if the event is considered as New, the door prizes should all be new. However, the relationship between DoorPrize and Sponsor is most likely a many-to-many association where one sponsor will likely be associated with many different door prizes across different events.
With ownership, if a client consumer is generating new IDs for entities (not recommended, it's better to leverage things like Identity columns and let the database manage that) then you might need to check that new DoorPrize records are not in the DB. The point here wouldn't be to replace existing Door Prizes if found, but to throw a data exception since we expect to be adding these new children:
Example if DoorPrizes are "owned" by Events (1-to-many relationship) but DoorPrizeIds are set by the consumer such as using a meaningful key or Guid.New()
var doorPrizeIds = newEvent.SelectMany(e => e.DoorPrize.Id).ToList();
var doorPrizeExists = _context.DoorPrizes.Any(dp => doorPrizeIds.Contains(dp.Id));
if (doorPrizeExists)
return BadRequest(new ConstraintException("One or more door prizes already exists"))
Dealing with associations requires a bit more attention. If DoorPrizes are expected to exist and are associated with a new Event then we need to locate those. If this request needs to handle that new DoorPrize entities might be created as part of this request, then that would need to be handled as well. As a general rule it is better to handle things more atomically where creating an Event that associates with door prizes would be responsible for just that. If there was an operation to create a new Door Prize then that would be handled by a separate call.
Example if DoorPrizes are "associated" to Events (many-to-many relationship)
var doorPrizeIds = newEvent.SelectMany(e => e.DoorPrize.Id)
.ToList();
var existingDoorPrizes = await _context.DoorPrizes
.Where(dp => doorPrizeIds.Contains(dp.Id))
.ToListAsync();
var existingDoorPrizeIds = existingDoorPrizes.Select(dp => dp.Id).ToList();
var doorPrizesToExclude = newEvent.SelectMany(e => e.DoorPrize)
.Where(dp => existingDoorPrizeIds.Contains(dp.Id))
.ToList();
foreach(var doorPrize in doorPrizesToExclude)
newEvent.DoorPrizes.Remove(doorPrize);
foreach(var doorPrize in existingDoorPrizes)
newEvent.DoorPrizes.Add(doorPrize);
What this gives us is a list of matching real Door Prize entities to associate. We will want to associate these in place of the data that came in with the new event. Any door prizes that might be new would be added when the event is added. The final step here will apply to both scenarios which will be to associate the sponsors to any new DoorPrize. In the one-to-many scenario that would be every door prize, in the many-to-many that would just be the non-existing ones that might be added:
1-to-many example:
var sponsorIds = newEvent
.SelectMany(e => e.DoorPrizes.Select(dp => dp.Sponsor.Id))
.Distinct();
var sponsors = await _context.Sponsors
.Where(s => sponsorIds.Contains(s.Id))
.ToListAsync();
foreach(var doorPrize in newEvent.DoorPrizes)
{
var sponsor = sponsors.SingleOrDefault(s => s.Id == doorPrize.Sponsor.Id);
if(sponsor == null)
return BadRequest(new ConstraintException("One or more door prizes was invalid."))
doorPrize.Sponsor = sponsor;
}
Many-to-many example:
1-to-many example:
var newDoorPrizes = newEvent.DoorPrizes.Where(dp => !existingDoorPrizeIds.Contains(dp.Id)).ToList();
if(newDoorPrizes.Any())
{
var sponsorIds = newDoorPrizes.Select(dp => dp.Sponsor.Id))
.Distinct();
var sponsors = await _context.Sponsors
.Where(s => sponsorIds.Contains(s.Id))
.ToListAsync();
foreach(var doorPrize in newEvent.DoorPrizes)
{
var sponsor = sponsors.SingleOrDefault(s => s.Id == doorPrize.Sponsor.Id);
if(sponsor == null)
return BadRequest(new ConstraintException("One or more door prizes was invalid."))
doorPrize.Sponsor = sponsor;
}
}
A similar operation to deal with associations for the Sponsor, the difference just being if the DoorPrizes are associations, we only want to do the substitution for Sponsors on newly added door prizes. The door prizes we re-associated from context tracked entities will already have valid sponsors.
Later, when you perform updates, it is a similar process, except you would expect to fetch the existing entity, but also pre-fetch the associated details with eager loading:
var existingEntry = _context.Entries
.Include(e => e.DoorPrizes)
.Single(e => e.Id == entryId);
This will throw if the entry isn't found which you can catch, or call .SingleOrDefault and check for #null to return your BadRequest if you prefer doing it inline. From there it is much the same process by where you can inspect the details coming un with the existingEntry to determine if DoorPrizes need to be updated, added, or removed. Again, for adding DoorPrizes the same process to re-associate Sponsors with actual tracked instances.
The important thing when updating entity graphs (parent-child relationships or associations) is to differentiate between whether the higher level entity "owns" the relationship, or if it is an association between entities that may already exist in the database. You will want to avoid code that detaches tracked entities and then does things like setting a passed in entity state to Modified to be saved. This will lead to all manners of problems where you overwrite data you don't intend to change, or exceptions when EF/SQL get told to do something invalid.
I have my entity defined like this:
public class Entity : BaseModel // Has the already ID defined
{
private int? companyId;
public Company? Company { get; set; }
public int? CompanyId {
get => this.companyId == 0 ? null : this.companyId; // I tried this for debugging purposes to force this value to "null" -> made no difference
set => this.companyId = value;
}
}
public class Company : BaseModel // Has the already ID defined
{
public IEnumerable<Entity> Entities { get; set; } = new List<Entity>();
}
Anyway, if I set the CompanyId to null, my DB throws an exception with the message: "FOREIGN KEY constraint failed". If the CompanyId is set to, e.g. 123, the relationship is resolved accordingly.
I mean, it makes sense, that EF cannot find null in my DB, but how do I want to set an optional value otherwise? I am using code first annotations only, hence my OnModelCreating of my context is completely empty.
How are you loading the entities in the first place? Are you loading an Entity by ID and trying to dis-associate it from a company, or have you loaded a company with it's entities and trying to remove one association?
Normally when working with relations where you have navigation properties, you want to de-associate them (or delete them) via the navigation properties, not the FK properties. For instance if loading a company and wanting to de-associate one of the entities you should eager-load the entities then remove the desired one from the collection:
var company = _context.Companies.Include(c => c.Entitites).Single(c => c.Id == companyId);
var entityToRemove = company.Entities.SingleOrDefault(e => e.Id == entityId);
if(entityToRemove != null)
company.Entities.Remove(entityToRemove);
_context.SaveChanges();
Provided that the relationship between Company and Entity is set up properly as an optional HasMany then provided these proxies are loaded, EF should work out to set the entityToRemove's FK to null.
If you want to do it from the Entity side:
var entityToRemove = _context.Entities.Include(e => e.Company).Single(e => e.Id == entityId);
entityToRemove.Company = null;
_context.SaveChanges();
That too should de-associate the entities. If these don't work then it's possible that your mapping is set up for a required relationship, though I am pulling this from memory so I might need to fire up an example to verify. :) You also should be checking for any code that might set that CompanyId to 0 when attempting to remove one, whether that might be happening due to some mapping or deserialization. Weird behaviour like that can occur when entities are passed around in a detached state or deserialized into controller methods. (which should be avoided)
Update: Code like this can be very dangerous and lead to unexpected problems like what you are encountering:
public virtual async Task<bool> Update(TModel entity)
{
Context.Update(entity);
await Context.SaveChangesAsync();
return true;
}
Update() is typically used for detached entities, and it will automatically treat all values in the entity as Modified. If model was already an entity tracked by the Context (and the context is set up for change tracking) then it is pretty much unnecessary. However, something in the calling chain or wherever has constructed the model (i.e. Entity) has set the nullable FK to 0 instead of #null. This could have been deserialized from a Form etc. in a view and sent to a Controller as an integer value based on a default for a removed selection. Ideally entity classes should not be used for this form of data transfer from view to controller or the like, instead using a POCO view model or DTO. To correct the behaviour as your code currently is, you could try the following:
public async Task<bool> UpdateEntity(Entity entity)
{
var dbEntity = Context.Set<Entity>().Include(x => x.Customer).Single(x => x.Id == entityId);
if (!Object.ReferenceEquals(entity, dbEntity))
{ // entity is a detached representation so copy values across to dbEntity.
// TODO: copy values from entity to dbEntity
if(!entity.CustomerId.HasValue || entity.CustomerId.Value == 0)
dbEntity.Customer = null;
}
await Context.SaveChangesAsync();
return true;
}
In this case we load the entity from the DbContext. If this method was called with an entity tracked by the DbContext, the dbEntity would be the same reference as entity. In this case with change tracking the Customer/CustomerId reference should have been removed. We don't need to set entity state or call Update. SaveChanges should persist the change. If instead the entity was a detached copy deserialized, (likely the case based on that 0 value) the reference would be different. In this case, the allowed values in the modified entity should be copied across to dbEntity, then we can inspect the CustomerId in that detached entity for #null or 0, and if so, remove the Customer reference from dbEntity before saving.
The caveats here are:
This won't work as a pure Generic implementation. To update an "Entity" class we need knowledge of these relationships like Customer so this data service, repository, or what-have-you implementation needs to be concrete and non-generic. It can extend a Generic base class for common functionality but we cannot rely on a purely Generic solution. (Generic methods work where implementation is identical across supported classes.)
This also means removing that attempt at trying to handle Zero in the Entity class. It should just be:
public class Entity : BaseModel
{
public Company? Company { get; set; }
[ForeignKey("Company")]
public int? CompanyId { get; set; }
// ...
}
Marking Foreign Keys explicitly is a good practice to avoid surprises when you eventually find yourself needing to break conventions that EF accommodates in simple scenarios.
I'm getting this error, but I can't seem to find a proper resolution.
The instance of entity type 'Assignment' cannot be tracked because
another instance with the same key value for {'OwnerUserId'} is
already being tracked. When attaching existing entities, ensure that
only one entity instance with a given key value is attached.
Functionally all I'm trying to do is insert some records with a composite primary key. Basically I just need to assign users to each other. I'm telling the context (afaik) to not do tracking, yet the error still seems to happen.
using (var context = new MyDbContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
//Get students with no assignments
var missingAssignments = model.Students.Where(x => x.AssignedToRead.Count < 2);
foreach (var s in missingAssignments)
{
var need = 2 - s.AssignedToRead.Count();
List<Guid> assigned = new List<Guid>();
assigned.AddRange(s.AssignedToRead.Select(x => x.UserId));
try
{
for (var i = 0; i < need; i++)
{
//Get a random reader (who's not them)
var shuffle = model.Students.Where(x => x.UserId != s.UserId).Where(x => !assigned.Contains(x.UserId)).ToList();
shuffle.Shuffle();
var rando = shuffle.FirstOrDefault();
var newAssignment = new Assignment() {
OwnerUserId = s.UserId,
ReaderUserId = rando.UserId
};
assigned.Add(rando.UserId);
context.Add(newAssignment);
context.SaveChanges();
}
}catch(Exception ex)
{
//Handle
}
}
}
I've validated I'm not inserting duplicate records (like 2 objects with the same PKs)
Is the problem in the mapping maybe?
modelBuilder.Entity<Assignment>(entity =>
{
entity.HasKey(e => new { e.OwnerUserId, e.ReaderUserId });
entity.ToTable("assignments");
entity.Property(e => e.OwnerUserId).HasColumnName("ownerUserId");
entity.Property(e => e.ReaderUserId).HasColumnName("readerUserId");
entity.Property(e => e.ModifiedDate)
.HasColumnName("modifiedDate")
.HasDefaultValueSql("(getdate())");
entity.HasOne(d => d.Owner)
.WithOne()
.HasPrincipalKey<Assignment>(d => d.OwnerUserId)
.HasForeignKey<AspnetUsers>(d => d.UserId)
.HasConstraintName("FK_users_owner");
entity.HasOne(d => d.Reader)
.WithOne()
.HasPrincipalKey<Assignment>(d => d.ReaderUserId)
.HasForeignKey<AspnetUsers>(d => d.UserId)
.HasConstraintName("FK_users_reader");
});
I don't understand why it would be tracking when I tell it not to
EDIT It appears the issue is with my Navigation Property mapping on the context? If i remove that entirely the Insert works fine, and I still have functioning Nav Properties on the object... I'm so confused, I thought it had to be told what to do?
I believe any entity added using Add will be tracked, the tracking behaviour is for entities that are queried from the DB. Does the error happen on the first instance you add, or on the second, or later?
I suspect the issue is probably with the HasOne.WithOne mapping between Assignment and User. I think this should be a HasOne.WithMany as a One-to-One typically defaults to being between PKs. (Though /w EF Core I believe you can nominate a non-PK 1-to-1) This mapping may be overruling the PK definition or otherwise adding a unique requirement that the tracking is considering. One User can have many assignments, but each assignment has one owner.
Another thing to check: In your entity class definition for Assignment, do you happen to have any [Key] attributes defined that might be tripping up the EF mapping? Your modelBuilder key mapping otherwise looks fine.
If I try to delete a "child" row I always get an exception. Here is a snipset:
using (var context = new CompanyContext())
{
ItemType itemType = context.ItemTypes.FirstOrDefault(i => i.Name == "ServerType");
ItemTypeItem itemTypeItem = itemType.Items.FirstOrDefault(i => i.Name == "DatabaseServer");
itemType.Items.Remove(itemTypeItem);
context.SaveChanges(); <=== exception!
}
The following exception is thrown on the SaveChanges() method.
"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."
Entity Configuration
public class ItemTypeConfiguration : NamedEntityConfiguration<ItemType>
{
public ConfigurationColumn ParentIDColumn;
public ConfigurationColumn ValidationPatternColumn;
public ItemTypeConfiguration() : base()
{
ParentIDColumn = new ConfigurationColumn() { Name = "ParentID", Ordinal = base.LastOrdinalPosition + 1 };
ValidationPatternColumn = new ConfigurationColumn() { Name = "ValidationPattern", Length = 1024, Ordinal=base.LastOrdinalPosition + 2};
this.Property(t => t.ParentID)
.HasColumnName(ParentIDColumn.Name)
.HasColumnOrder(ParentIDColumn.Ordinal);
this.HasOptional(t => t.Parent).WithMany().HasForeignKey(u => u.ParentID).WillCascadeOnDelete(false);
this.Property(t => t.ValidationPattern)
.HasColumnName(ValidationPatternColumn.Name)
.HasColumnOrder(ValidationPatternColumn.Ordinal)
.HasMaxLength(ValidationPatternColumn.Length);
}
...
public class ItemTypeItemConfiguration : NamedEntityConfiguration<ItemTypeItem>
{
public ConfigurationColumn ItemTypeIDColumn;
public ItemTypeItemConfiguration() : base()
{
ItemTypeIDColumn = new ConfigurationColumn(){Name="ItemTypeID", IsRequired=true, Ordinal= base.LastOrdinalPosition+1};
this.Property(t => t.ItemTypeID)
.HasColumnName(ItemTypeIDColumn.Name)
.HasColumnOrder(ItemTypeIDColumn.Ordinal);
this.HasRequired(t => t.ItemType).WithMany(t=>t.Items).HasForeignKey(u => u.ItemTypeID).WillCascadeOnDelete(true);
}
...
I found the blog but I don't have the "DeleteObject" method.
http://blog.clicdata.com/2013/07/04/the-operation-failed-the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-properties-is-non-nullable/
Any ideas? Thank you.
You need to delete the ItemTypeItem.
It is not possible to just remove it from the Items list as it cannot exist by itself, because it has a non-nullable foreign key referencing ItemType (ItemTypeID).
To delete the ItemTypeItem add
context.Entry(itemTypeItem).State = EntityState.Deleted;
In the entity framework 6.0 if you remove the entity from the main context set it will work. For example to remove an investment entity you would do the following:
context.Investments.Remove(entity);
context.SaveChanges();
This is different than attempting to remove the entity from its parent/owner, as the following:
bankAccount.Investments.Remove(entity);
context.SaveChanges();
This will throw the relationship could not be changed exception listed above. Hope this helps.
In entity 6.0 there is a difference between:
context.Investments.Remove(entity);
and
context.Entry(entity).State = EntityState.Deleted;
When using the first and cascading deletes are enabled, EF will internally perform the necessary deletes of child collections.
When using the second option, EF will not handle the necessary deletes, but let you handle the rebinding/deletion of these child objects.
This issue arise because we try to delete the parent table still child table data is present. We solve the problem with help of cascade delete.
In model Create method in dbcontext class.
modelBuilder.Entity<Job>()
.HasMany<JobSportsMapping>(C => C.JobSportsMappings)
.WithRequired(C => C.Job)
.HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
modelBuilder.Entity<Sport>()
.HasMany<JobSportsMapping>(C => C.JobSportsMappings)
.WithRequired(C => C.Sport)
.HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);
After that,In our API Call
var JobList = Context.Job
.Include(x => x.JobSportsMappings) .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();
Cascade delete option delete the parent as well parent related child table with this simple code. Make it try in this simple way.
Remove Range which used for delete the list of records in the database Thanks
The other answers describing why the error occurs are correct, but it is actually possible to get EF to fully delete the child when .Remove is called on the parent object's collection of children, you don't need to go directly to the child entity's table in the DB context and delete it from that.
To get that to work you need to set up an Identifying Relationship, as described in this answer.
I have the following piece of code
private void DoAddPropertyType()
{
var ctx = Globals.DbContext;
var propType = new PropertyType()
{
ID = Guid.NewGuid(),
Name = "NewType",
Description = "New Property Type",
ModifiedDate = DateTime.Now
};
ctx.AddToPropertyTypes(propType);
PropertyTypes.Add(propType);
}
Globals.DbContext provides a static reference to the objectcontext initiated on startup. For some reason the ctx.AddToPropertyTypes(propType); bit does not add the entity to the context. If I breakpoint after that line and browse the ctx.PropertyTypes entity set it is not there. Any ideas?
EDIT 1:
If I add a ctx.SaveChanges() after the ctx.AddToPropertyTypes(propType) and step the actual adding appears to happen only once SaveChanges execute. This however does not suit my requirements as I want to first validate objects prior to saving and wanted to iterate through the entities in the entity set. Does any one know of an alternative approach?
So that is the point of your issue. ctx.PropertyTypes is not a real collection - it is entrance to the database and your "browsing" actually executes query to the database where your new object was not yet stored. If you want to find a new object added to the context without saving it first you must search the object inside the ObjectStateManager:
var entity = ctx.ObjectStateManager
.GetObjectStateEntries(EntityState.Added)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<PropertyType>()
.SingleOrDefault(p => p.ID == ...);