0
votes

Hi I am getting this error message, that is only triggered in one particular case.

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.

System.InvalidOperationException was unhandled by user code
HResult=-2146233079 Message=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. Source=EntityFramework
StackTrace: at System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges(SaveOptions options) at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction) at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options) at System.Data.Entity.Internal.InternalContext.SaveChanges() at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() at System.Data.Entity.DbContext.SaveChanges()

.....

I have a credit system where my user can buy credits to perform a check. Everytime the check is performed the number of credits a user has is decremented.

The User table has a FK creditId. CreditId is the PK for the Credit table. This can be thought of as as a 1 to 1 relationship, but I have only defined the relationship in the User model. I separated the credit into a separate table so that I can keep track of when credits for a user were changed and who made the change.

When a user buys credits or performs a new check everything works fine. But if the user tries perform a re-check. It causes the error. I can't understand how this happens? I have a feeling that there is a conflict in the Context but I can't figure out where.

public abstract class Repository
{
    private readonly DbContext _dataContext;

    protected Repository(DbContext dataContext)
    {
        _dataContext = dataContext;
    }

    public int SaveChanges()
    {
        return _dataContext.SaveChanges();
    }
}

This is called in the method

private int SubtractFromCredit(int amount, User user)
    {
        var now = DateTime.UtcNow;

        var credits = user.Credits;

        credits.Amount -= amount;
        credits.ModifiedDate = now;
        credits.ModifiedBy = user.Id;

        _creditsRepository.SaveChanges();

        return credits.Amount;
    }

I'm not exactly sure why this is failing because the Credits table is only a FK in the User table. Adding credits is fine and subtracting credits is also fine when I am performing a re-check it fails.

At the end of the day I am just trying to do a simple update row. I don't see how this can cause a conflict with FKs

Any help with what to look for when troubleshooting this issue would be great

2

2 Answers

1
votes

Can you add those entity definitions to your question?

If you are using the User to navigate to their credits then I suspect you would need to have two foreign key relationships. One is many to many for users and their credits. And other for Credits => modified by.

0
votes

Ok it turns out that the error was thrown in a misleading part of code. The error was thrown in

private int SubtractFromCredit(int amount, User user)
{
    var now = DateTime.UtcNow;

    var credits = user.Credits;

    credits.Amount -= amount;
    credits.ModifiedDate = now;
    credits.ModifiedBy = user.Id;

    // inside SaveChanges here 
    _creditsRepository.SaveChanges();

    return credits.Amount;
}

But it was caused by the line above where SubstractFromCredit was called.

tenantCheck.PreviousAddresses = _addressService.AddPreviousAddresses(addresses, tenantCheck, user.Id);

I can't explain why I did this or what I was thinking at the time. But the issue was that PreviousAddresses is a virtual ICollection and it is populated for me by EF. By manually assigning it to be only the new records for PreviousAddresses, EF didn't know what to do with the existing records for PreviousAddresses.

After writing this out I would speculate that the error was thrown in SubtractFromCredit because after assigning PreviousAddresses this was the next place that the Data Context was called to save changes to the database