1

I have a class with 2 circular references to its self, .NextVersion, and .PreviousVersion, that creates a "chain" of objects as declared below. When trying to use Add-Migration to create the database I get the following error:

Unable to determine the principal end of an association between the types 'SqlMetaQuery.Model.ScriptVersion' and 'SqlMetaQuery.Model.ScriptVersion'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

public class ScriptVersion
{
    public Guid ScriptVersionId { get; set; }
    public Guid ScriptId { get; set; }
    public string Body { get; set; }
    public DateTime CreatedDate { get; set; }
    public User CreatedBy { get; set; }

    public ScriptVersion NextVersion { get; set; }
    public ScriptVersion PreviousVersion { get; set; }
}

I found several answers to similar questions that say to use .HasOptional and .WithOptionalDependant to resolve the associations. This works for one of the navigation properties, but fails when I try to do it for the 2nd navigation property.

        modelBuilder.Entity<ScriptVersion>()
            .HasOptional(v => v.NextVersion)
            .WithOptionalDependent(v => v.PreviousVersion);

        modelBuilder.Entity<ScriptVersion>()
            .HasOptional(v => v.PreviousVersion)
            .WithOptionalDependent(v => v.NextVersion);

The navigation property 'PreviousVersion' declared on type 'SqlMetaQuery.Model.ScriptVersion' has been configured with conflicting foreign keys.

What am I doing wrong?

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76
  • Similar: http://stackoverflow.com/questions/24033924/creating-a-double-linked-list-in-entity-framework – Steve Greene Jan 28 '16 at 16:31
  • Is that question implying that I only need to configure one of the navigation properties? When I do that, the migration is created without errors, but running it in to the database doesn't create the database structure I would expect. – Bradley Uffner Jan 28 '16 at 16:36
  • Specifically, the ScriptVersion table only has a field for PreviousVersionId, no NextVersionId; and it needs to be nullable, but is created as required. – Bradley Uffner Jan 28 '16 at 16:48
  • Yeah, that was a slight variation. Was thinking you might add MapKeys to both as a possible workaround. http://stackoverflow.com/questions/21878457/can-you-customise-foreign-key-names-in-self-referencing-entities-in-entity-frame – Steve Greene Jan 28 '16 at 17:02

1 Answers1

0

I had to change the entity but here is a solution. The trick is to add one or more additional "fake" properties to make the job.

Entity

public class ScriptVersion
{
    private ScriptVersion _nextVersion;
    private ScriptVersion _previousVersion;

    public Guid ScriptVersionId { get; set; }
    public Guid ScriptId { get; set; }
    public string Body { get; set; }
    public DateTime CreatedDate { get; set; }
    public User CreatedBy { get; set; }

    public ScriptVersion NextVersion
    {
        get { return _nextVersion; }
        set
        {
            _nextVersion = value;
            if (_nextVersion != null && _nextVersion.PreviousVersion != this)
            {
                _nextVersion.PreviousVersion = this;
            }
        }
    }

    public ScriptVersion PreviousVersion
    {
        get { return _previousVersion; }
        set
        {
            _previousVersion = value;
            if (_previousVersion != null && _previousVersion.NextVersion != this)
            {
                _previousVersion.NextVersion = this;
            }
        }
    }

    internal ScriptVersion InternalNextVersion
    {
        get { return NextVersion; }
    }

    internal ScriptVersion InternalPreviousVersion
    {
        get { return PreviousVersion; }
    }
}

Model

modelBuilder.Entity<ScriptVersion>()
    .HasOptional(v => v.NextVersion)
    .WithOptionalDependent(x => x.InternalPreviousVersion);

modelBuilder.Entity<ScriptVersion>()
    .HasOptional(v => v.PreviousVersion)
    .WithOptionalDependent(x => x.InternalNextVersion);
Jonathan Magnan
  • 10,874
  • 2
  • 38
  • 60