EntityFramework6 "FOREIGN KEY constraint failed" on nullable foreign key - entity-framework

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.

Related

Do I need a foreign key in the EF core code first?

I'm new at code first in entity framework and reading up on relationships, I see everyone does it differently. It might be because of earlier versions, might be the same or might be because of performance.
Let's say I have two tables Company and User.
I would set the company-to-user relationship like this:
public List<User> Users { get; set; } = new List<User>();
Then if I from the user-to-company perspective needed to find the company, I would have this in the User:
public Company Company { get; set; }
And do this query:
return await _clientContext.Users.Where(x => x.Company.Id == companyId).ToListAsync();
Or I could have this:
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
And have this query:
return await _clientContext.Users.Where(x => x.CompanyId == companyId).ToListAsync();
Also some define Company with the keyword virtual like this:
public virtual Company Company { get; set; }
I'm not sure if every scenario is the same and doing x.CompanyId instead of x.Company.Id would actually be the same. What is used normally?
I generally recommend the first option using Shadow Properties for FKs over the second when using navigation properties. The main reason is that with the second approach there are two sources of truth. For instance with a Team referencing a Coach, some code may use team.CoachId while other code uses team.Coach.CoachId. These two values are not guaranteed to always be in sync. (depending on when you happen to check them when one or the other is updated.)
Updating references between entities via a FK property can have varied behaviour depending on whether the referenced entity is loaded or not.
What is the expected difference between if want to update a team's coach:
var teamA = context.Teams.Single(x => x.TeamId == teamId);
If Team has a Coach navigation property and a CoachId FK reference I could do...
teamA.CoachId = newCoachId;
If TeamA's old coach ID was 1, and the newCoachId = 2, what do you think happens if I have code that lazy loads the coach before SaveChanges?
var coachName = teamA.Coach.Name;
You might expect that since the Coach hadn't been loaded yet it would load in Coach #2's name, but it loads Coach #1 because the change hasn't been committed even though teamA.CoachId == 2. If you check the Coach reference after SaveChanges you get Coach #2.
Depending on whether lazy loading is enabled or not you can get a bit strange behaviour by setting a FK property where navigation properties are nulled. Even when eager loading, changing a FK property will potentially trigger a new lazy load if that new entity isn't already tracked:
var teamA = context.Teams.Include(x => x.Coach).Single(x => x.TeamId == teamId);
teamA.CoachId == newCoachId;
var coachName = teamA.Coach.Name; // Still points to Coach #1's name as expected.
context.SaveChanges();
coachName = teamA.Coach.Name; // Triggers lazy load and return new coach's name.
Saving a FK against an entity that has eager loaded the reference does not automatically re-populate referenced entities. So for instance if you have lazy loading disabled, the same above code:
context.SaveChanges();
coachName = teamA.Coach.Name; // Potential NullReferenceException on teamA.Coach.
This will potentially trigger a null reference exception unless the new coach happens to be tracked by the DbContext prior to SaveChanges being called. If the DbContext is tracking the entity, the new reference will be swapped in on SaveChanges, otherwise it is nulled. (With lazy loading this is covered by the new lazy load call after it was nulled)
When working with navigation properties my default recommendation is to hide FK properties as Shadow Properties. (For EF6 this means using .Map(x => x.MapKey()). For relationships where I only care about the ID, I will expose the FK with no navigation property. So, one or the other. (Such as lookups or bounded contexts where I want raw speed.)
I will deviate sparingly from this for exposing FKs for relationships I may inspect by ID frequently, and treat it as read-only, but still have infrequent need of the navigation property. An example of this would be CreatedBy / CreatedByUserId. Many queries may inspect the CreatedByUserId for data filtering, while some projections may want the CreatedBy.Name etc. A record's CreatedBy doesn't change so I avoid potential pitfalls of the data getting out of sync.
Your second scenario is used normally.
i.e.
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
And have this query:
return await _clientContext.Users.Where(x => x.CompanyId == companyId).ToListAsync();

Issues with Automatically setting created and modified date on each record in EF Core

Using ASP.NET Core 2.2 with EF Core, I have followed various guides in trying to implement the automatic creation of date/time values when creating either a new record or editing/updating an existing one.
The current result is when i initially create a new record, the CreatedDate & UpdatedDate column will be populated with the current date/time.
However first time I edit this same record, the UpdatedDate column is then given a new date/time value (as expected) BUT for some reason, EF Core is wiping out the value of the original CreatedDate which results in SQL assigning a default value.
Required result I need as follows:
Step 1: New row created, both CreatedDate & UpdatedDate column is given a date/time value (this already works)
Step 2: When editing and saving an existing row, I want EF Core to update the UpdatedDate column with the updated date/time only, BUT leave the other CreatedDate column unmodified with the original creation date.
I'm using EF Core code first, and do no want to go down the fluent API route.
One of the guides i was partially following is https://www.entityframeworktutorial.net/faq/set-created-and-modified-date-in-efcore.aspx but neither this or other solutions I've tried is giving the result I am after.
Baseclass:
public class BaseEntity
{
public DateTime? CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
}
DbContext Class:
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
var entries = ChangeTracker.Entries().Where(E => E.State == EntityState.Added || E.State == EntityState.Modified).ToList();
foreach (var entityEntry in entries)
{
if (entityEntry.State == EntityState.Modified)
{
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
else if (entityEntry.State == EntityState.Added)
{
entityEntry.Property("CreatedDate").CurrentValue = DateTime.Now;
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
UPDATE FOLLOWING ADVICE FROM STEVE IN COMMENTS BELOW
I spent a bit more time debugging today, turns out the methods I posted above are appear to be functioning as expected i.e. when editing an existing row and saving it, only the entityEntry.State == EntityState.Modified IF statement is being called. So what I'm finding is that after saving the entity, the CreatedDate column is being overwitten with a Null value, I can see this by watching the SQL explorer after a refresh. I believe the issue is along the lines of what Steve mentions below "If it is #null then this might also explain the behavior in that it is not being loaded with the entity for whatever reason."
But i'm a little lost in tracing where this CreatedDate value is being dropped somewhere through edit/save process.
Image below shows the result at the point just before the entity is saved following an update. In the debugger I'm not quite sure where to find the entry of the CreatedDate to see what value is held at this step, but it appears to be missing from the debugger list so wandering whether somehow it doesn't know about the existence of this field when saving.
Below is the method I have in my form 'Edit' Razor page model class:
public class EditModel : PageModel
{
private readonly MyProject.Data.ApplicationDbContext _context;
public EditModel(MyProject.Data.ApplicationDbContext context)
{
_context = context;
}
[BindProperty]
public RuleParameters RuleParameters { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
RuleParameters = await _context.RuleParameters
.Include(r => r.SystemMapping).FirstOrDefaultAsync(m => m.ID == id);
if (RuleParameters == null)
{
return NotFound();
}
ViewData["SystemMappingID"] = new SelectList(_context.SystemMapping, "ID", "MappingName");
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(RuleParameters).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!RuleParametersExists(RuleParameters.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool RuleParametersExists(int id)
{
return _context.RuleParameters.Any(e => e.ID == id);
}
}
Possibly one of the reasons for this issue is the fact that I have not included the CreatedDate field in my Edit Razor Page form, so when I update the entity which in turn will run the PostAsync method server side, there is no value stored for the CreatedDate field and therefore nothing in the bag by the tine the savechangesasync method is called in my DbContext Class. But I also didn't think this was necessary? otherwise I'd struggle to see what value there is in the this process of using an inherited BaseEntity class i.e. not having to manually add the CreatedDate & UpdatedDate attribute to every model class where I want to use it...
It may be easier to just give your BaseEntity a constructor:
public BaseEntity()
{
UpdatedDate = DateTime.Now;
CreatedDate = CreatedDate ?? UpdatedDate;
}
Then you can have your DbContext override SaveChangesAsync like:
public override Task<int> SaveChangesAsync(
bool acceptAllChangesOnSuccess,
CancellationToken token = default)
{
foreach (var entity in ChangeTracker
.Entries()
.Where(x => x.Entity is BaseEntity && x.State == EntityState.Modified)
.Select(x => x.Entity)
.Cast<BaseEntity>())
{
entity.UpdatedDate = DateTime.Now;
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, token);
}
Possibly one of the reasons for this issue is the fact that I have not included the CreatedDate field in my Edit Razor Page form, so when I update the entity which in turn will run the PostAsync method server side, there is no value stored for the CreatedDate field and therefore nothing in the bag by the tine the savechangesasync method is called in my DbContext Class.
That's true.Your post data does not contains the original CreatedDate,so when save to database, it is null and could not know what the exact value unless you assign it before saving.It is necessary.
You could just add below code in your razor form.
<input type="hidden" asp-for="CreatedDate" />
Update:
To overcome it in server-side,you could assign data manually:
public async Task<IActionResult> OnPostAsync()
{
RuleParameters originalData = await _context.RuleParameters.FirstOrDefaultAsync(m => m.ID == RuleParameters.ID);
RuleParameters.CreatedDate = originalData.CreatedDate;
_context.Attach(RuleParameters).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
I don't suspect EF is doing this, but rather your database, or you're inadvertently inserting records instead of updating them.
A simple test: Put break-points in your SaveChangesAsnc method within both the Modified and Added handlers and then run a unit test that loads an entity, edits it, and saves. Which breakpoint is hit? If the behavior seems to be normal with a simple unit test, repeat again with your code.
If the Modified breakpoint is hit, and only the Modified handler is hit then check the state of the CreatedDate value in the entity modified. Does it still reflect the original CreatedDate? If yes, then it would appear that something in your schema will be overwriting it on save. If no then you have a bug in your code that has caused it to update. If it is #null then this might also explain the behaviour in that it is not being loaded with the entity for whatever reason. Check that the property has not been configured as something like a Computed property.
If the Added breakpoint is hit at all, then this would point at a scenario where you're dealing with a detached entity, such as an entity that was read from a different DB Context and being associated to another entity in the current DB Context and saved as a byproduct. When a DbContext encounters an entity that was loaded and disassociated with a different DbContext, it will treat that entity as a completely new entity and insert a new record. The biggest single culprit for this is invariably MVC code where people pass entities to/from views. Entity references are loaded in one request, serialized to the view, and then passed back on another request. Devs assume they are receiving an entity that they can just associate to a new entity and save, but the Context of this request doesn't know about that entity, and that "entity" isn't actually an entity, it is now a POCO shell of data that the serializer created. It's no different to you newing up a new class and populating fields. EF won't know the difference. The result of this is you will trip the Added condition for your entity, and after completion you will have a duplicate record. (with different PK if EF is configured to treat PKs as Identity)
So an example is an Order screen: When presenting a screen to create a new order I may have loaded the Customer and passed that to the view to display customer information and will want to associate to the new order:
var customer = context.Customers.Single(x => x.CustomerId == 15);
var newOrder = new Order { Customer = customer };
return View(newOrder);
This looks innocent enough. When we go to save the new order after setting their details:
public ActionResult Save(Order newOrder)
{
context.Orders.Add(newOrder);
newOrder.Customer.Orders.Add(newOrder);
context.SaveChanges();
// ...
}
newOrder had a reference to Customer #14, so all looks good. We're even associating the new order to the customer's order collection. We might even want to have updated fields on the customer record to reflect a change to the Modified date. However, newOrder in this case, and all associated data including .Customer are plain 'ol C# objects at this point. We've added the new order to the Context, but as far as the context is concerned, the Customer referenced is also a new record. It will ignore the Customer ID if that is set as an Identity column and it will save a brand new Customer record (ID #15 for example) with all of the same details as Customer ID 14 and associate that to the new order. It can be subtle and easy to miss until you start querying Customers and spotting duplicate looking rows.
If you are passing entities to/from views, I'd be very wary of this gotcha. Attaching and setting modified state is one option, but that involves trusting that the data has not been tampered with. As a general rule, calls to update entities should never pass entities & attach them, but rather re-load those entities, validate row version, validate the data coming in, and only copy across fields you expect should ever be modified before saving the entity associated to the DbContext.
Hopefully that gives you a few ideas on things to check to get to the bottom of the issue.

Relations between complex type not getting updated

So with entity framework I'm trying to update two existing entities.
There I've the main object something like:
public class MainObject
{
public string Name { get; set; }
public virtual SmallObject Part { get; set;}
}
public class SmallObject
{
public string Name { get; set; }
}
In the repository I first check if the SmallObject already exists in the database by:
MainObject.Part = (from s in repoSmallObject.GetAll()
where s.name == MainObject.Part.Name
select s).FirstOrDefault();
Then finally I call the update method in my GenericRepository
repoMainObject.Update(MainObject)
which is defined as a generic repository method:
dbSet.Attach(entity)
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
But the relationship doesn't get updated. Why is that? Both objects are attached to context not?
*Edit: The two repo's are injected with the same Context.
And strangely enough the Add method works and also updates the relationship.
When you set
context.Entry(entity).State = EntityState.Modified;
you need at least to set the state after and before updates (i.e. context.Entry(mainObject).CurrentValues and OriginalValues) so EF can build the right UPDATE query (with right WHERE clause).
It works if you set
context.Entry(entity).State = EntityState.Added;
because EF needs just to generate an INSERT query.
I don't know exactly why you need it but usually I prefer to attach the object to the DbSet and modify the properties so EF handles various states.
dbSet.Attach(MainObject)
MainObject.Part = (from s in repoSmallObject.GetAll()
where s.name == MainObject.Part.Name
select s).FirstOrDefault();
(In your case does not work because MainObject.Part.Name does not change)
The attached object should have the same values of the database otherwise you have a concurrency exception.
BTW, why you don't read the old object (MainObject) from the DB than work on it???

How to get navigation property of foreign key when calling SaveChanges()?

I'm using EF5 code first.
We have a method
LogHistoryTracking(DbEntityEntry entity)
to log changes when SaveChanges is called.
At SaveChanges, we get the changed entities and pass into LogHistoryTracking
var changedEntities = ChangeTracker.Entries().ToList();
But when I access
changedEntity.OriginalValues.PropertyNames
there is no properties for foreign keys object (only foreign key Id - but how can we get the data when there is only id here?).
I also tried to google for a solution, but this issue might be not so popular.
There is this article, but it does not work.
Appreciate any help. Thanks.
If you want to have your entity properties to be accessible you must 'Include' them prior to accessing them. Like in the following example which gets the orders of the first cutomer :
var orders = context.Customers
.Include("Orders")
.First().Orders;
In this example if you do not call .Include("Orders") you will not have Customer.Orders. The same goes if you have foreign key and forget to include the navigation property of the foreign key. This is because the key (the ID) is part of the object and the navigation property is not.
Let us see one real world example :
public class Employee : Entity
{
public virtual int CompanyUserId { get; set; }
public virtual CompanyUser CompanyUser { get; set; }
//... cut out for brevity
}
If you get the employees like this :
var employees = context.Employees;
You will not be able to access employees[0].CompanyUser after
context.SaveChanges() because of lazy loading. The connection is disposed after context.SaveChanges(), so no more data fetching.
But if you call :
var employees = context.Employees
.Include("CompanyUser")
.ToArray();
You will be able to access employees[0].CompanyUser.SomeProperty right away before context.SaveChanges regardless lazy loading because ToArray() will execute the query and fetch the entities with the "includes".
If you call :
var employees = context.Employees
.Include("CompanyUser");
Then you will have employee[0].CompanyUser.SomeProperty even after context.SaveChanges() with Lazy Loading because you have told EF to include "CompanyUser" property before executing the query. On execution EF will include the named property.
UPDATE
Intercepting DbContext can be done in at least two different ways.
First - override SaveChanges() or SaveChangesAsync because it is virtual:
public class MyDbContext : DbContext
{
public event Action<MyDbContext> SavingChanges = _ => { };
public override int SaveChanges()
{
this.SavingChanges(this);
return base.SaveChanges();
}
}
Second way without direct override is by hiding the DbContext inside interface like this one (this is from real project) :
public interface IUnitOfWork : IDisposable
{
void Commit();
}
Third way (somewhat different) is by intercepting the Db calls.
Fourth way exists but it depends on what IoC you use. If you use Castle Windsor you can use interceptors. I suppose that with every IoC there is its own way of intercepting this.

Entity Framework 4.1 Code First - Unable to remove a relationship between two entities

I have a Supplier entity, each Supplier object may reference another Supplier object as its 'parent'.
public class Supplier
{
public int? Id { get; set; }
public virtual Supplier Parent { get; set; }
}
This all works as expected until I attempt to remove the relationship, as in, this supplier no longer has a parent. I can change it from null to a particular supplier and I can set it to a different supplier but setting it to null is not persisted after SaveChanges().
supplier.Parent = null;
The foreign key 'ParentId' in the Supplier table is set as nullable. Explicitly defining the relationship doesn't help.
modelBuilder.Entity<Supplier>().HasOptional(s => s.Parent).WithMany();
I'm sure I'm missing something obvious.
Just found another place in my code where I do the exact same thing (that works) and found this;
// Must access property (trigger lazy-loading) before we can set it to null (Entity Framework bug!!!)
var colour = modelItem.Colour;
modelItem.Colour = null;
Did the same in the new code and it all works.
Instead of just modelBuilder.Entity<Supplier>().HasOptional(s => s.Parent).WithMany(); use the following modelBuilder.Entity<Supplier>().HasOptional(s => s.Parent).WithMany().HasForeignKey(x=>x.ParentId);
Otherwise it has no idea what the foreign key's name is