-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update Pipeline: Implement cycle breaking for optional relationships #1699
Comments
Spoke with @ajcvickers and @divega, and it looks like this will require some refactoring of the update pipeline to accomplish. It isn't a feature we supported in EF6 (Circular references required multiple calls to |
we should make sure not to expose PK values in the exception messages that will be thrown for unbreakable cycles |
Note to implementor: Also handle unique indexes and allow a custom cycle breaker for non-nullalbe properties and for filtered unique indexes. |
Hi @ajcvickers, We have been converted our banking solution which has 1500 entities from nhibernate to ef7. I think its a major issue our current domain model has this kind of relationship. Do we have any workaround to break cycle for optional/not optional relationships until this implementation is done ? |
@ajcvickers @AndriySvyryd To better understanding; Current exception: Unable to save changes because a circular dependency was detected in the data to be saved: Why circular its the same address instance with the same uniq id. "3111111112203474" public class MrcMerchant
{
public long Guid {get;set;}
public long DefaultAddressGuid {get;set;}
public MrcMerchantAddress MrcMerchantAddress {get;set;}
public List<MrcMerchantAddress> MrcMerchantAddresses {get;set;}
}
public class MrcMerchantAddress
{
public long Guid {get;set;}
public MrcMerchant MrcMerchant {get;set;}
public string AddressLine {get;set;}
} |
While this feature is not ready, you can make one side of the relationship optional and override DB context SaveChanges to:
Here is a basic code that handles simple cases of circular reference: Source code public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
var setCircularReferences = PostponeCircularReferences();
if (setCircularReferences == null)
{
return base.SaveChanges(acceptAllChangesOnSuccess);
}
var numAffected = 0;
using (var transaction = Database.CurrentTransaction == null ? Database.BeginTransaction() : null)
{
numAffected += base.SaveChanges(true);
setCircularReferences();
numAffected += base.SaveChanges(acceptAllChangesOnSuccess);
if (transaction != null)
transaction.Commit();
}
return numAffected;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
var setCircularReferences = PostponeCircularReferences();
if (setCircularReferences == null)
{
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
var numAffected = 0;
using (var transaction = Database.CurrentTransaction == null ? Database.BeginTransaction() : null)
{
numAffected += await base.SaveChangesAsync(true, cancellationToken);
setCircularReferences();
numAffected += await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
if (transaction != null)
await transaction.CommitAsync();
}
return numAffected;
}
private Action PostponeCircularReferences()
{
Action setCircularReferences = null;
foreach (var entry in ChangeTracker.Entries())
{
if (entry.State != EntityState.Added)
continue;
foreach (var reference in entry.References)
{
if (!(reference.Metadata is INavigation navigation))
continue;
if (navigation.ForeignKey.IsRequired)
continue;
var referenceTarget = reference.TargetEntry;
if (referenceTarget == null)
continue;
var hasCircularReference = referenceTarget.References.Any(targetRef => targetRef.CurrentValue == entry.Entity);
if (hasCircularReference)
{
var oldValue = reference.CurrentValue;
reference.CurrentValue = null;
setCircularReferences += () =>
{
reference.CurrentValue = oldValue;
};
}
}
}
return setCircularReferences;
} |
The following model currently throws saying it hit a circular dependency
The text was updated successfully, but these errors were encountered: