question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Update Pipeline: Implement cycle breaking for optional relationships

See original GitHub issue

The following model currently throws saying it hit a circular dependency

Test 'Microsoft.Data.Entity.SqlServer.FunctionalTests.NavigationTest.Duplicate_entries_are_not_created_for_navigations_E2E' failed:
    System.InvalidOperationException : A circular dependency was detected: 'Person' {'SiblingReverseId'} -> 'Person' {'Id'}, 'Person' {'LoverId'} -> 'Person' {'Id'}.
    Utilities\Multigraph.cs(338,0): at Microsoft.Data.Entity.Utilities.Multigraph`2.BatchingTopologicalSort(Func`2 formatCycle)
    Update\CommandBatchPreparer.cs(108,0): at Microsoft.Data.Entity.Relational.Update.CommandBatchPreparer.TopologicalSort(IEnumerable`1 commands)
    Update\CommandBatchPreparer.cs(50,0): at Microsoft.Data.Entity.Relational.Update.CommandBatchPreparer.<BatchCommands>d__1.MoveNext()
    Update\BatchExecutor.cs(66,0): at Microsoft.Data.Entity.Relational.Update.BatchExecutor.Execute(IEnumerable`1 commandBatches, RelationalConnection connection)
    RelationalDataStore.cs(82,0): at Microsoft.Data.Entity.Relational.RelationalDataStore.SaveChanges(IReadOnlyList`1 entries)
    ChangeTracking\Internal\StateManager.cs(353,0): at Microsoft.Data.Entity.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList`1 entriesToSave)
    ChangeTracking\Internal\StateManager.cs(308,0): at Microsoft.Data.Entity.ChangeTracking.Internal.StateManager.SaveChanges()
    DbContext.cs(312,0): at Microsoft.Data.Entity.DbContext.SaveChanges()
    NavigationTest.cs(73,0): at Microsoft.Data.Entity.SqlServer.FunctionalTests.NavigationTest.Duplicate_entries_are_not_created_for_navigations_E2E()
        [Fact]
        public void Duplicate_entries_are_not_created_for_navigations_E2E()
        {
            using (var ctx = new GoTContext())
            {
                ctx.Database.EnsureDeleted();
                ctx.Database.EnsureCreated();
                var cersei = new Person { Name = "Cersei" };
                var jamie = new Person { Name = "Jamie" };

                cersei.Lover = jamie;
                cersei.Siblings = new List<Person> { jamie, };

                ctx.People.AddRange(new[] { cersei, jamie, });
                ctx.SaveChanges();
            }
        }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public List<Person> Siblings { get; set; }
        public Person Lover { get; set; }
        public Person LoverReverse { get; set; }
        public Person SiblingReverse { get; set; }
    }

    public class GoTContext : DbContext
    {
        public DbSet<Person> People { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Person>().HasMany(p => p.Siblings)
                .WithOne(p => p.SiblingReverse).Required(false);
            modelBuilder.Entity<Person>().HasOne(p => p.Lover)
                .WithOne(p => p.LoverReverse).Required(false);
        }

        protected override void OnConfiguring(DbContextOptions options)
        {
            options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=StateManagerBug;Trusted_Connection=True;MultipleActiveResultSets=true");
        }
    }

Issue Analytics

  • State:open
  • Created 9 years ago
  • Reactions:22
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

7reactions
gokhanabataycommented, Jan 16, 2023

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 ?

0reactions
lucaslorentzcommented, Feb 4, 2023

While this feature is not ready, you can make one side of the relationship optional and override DB context SaveChanges to:

  • detect circular dependencies that are also optional and unset the value
  • save changes
  • restore the values that were unset
  • save changes

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;
        }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Chapter 4. Pipelines OpenShift Container Platform 4.10
This update adds a default-forbidden-env parameter in optional pipeline properties. The parameter includes forbidden environment variables that should not be ...
Read more >
Deprecations and removals by version | GitLab
The following GitLab features are deprecated and no longer recommended for use. Each deprecated feature will be removed in a future release. Some...
Read more >
MLOps: Continuous delivery and automation pipelines in ...
In practice, models often break when they are deployed in the real world. ... An optional additional component for level 1 ML pipeline...
Read more >
DependencyParser · spaCy API Documentation
The dependency parser jointly learns sentence segmentation and labelled dependency parsing, and can optionally learn to merge tokens that had been over- ...
Read more >
Azure DevOpsServer 2020 Update 1 Release Notes
Azure DevOps Server 2020 introduces a new pipeline run (build) retention model that works based on project-level settings. Azure DevOps Server ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found