简体   繁体   中英

Define column name for self-referencing List property in EF

I am defining a class based on a cataloguing Authority entry, which has a number of self referencing children, as follows:

public class Authority
{
    public long ID { get; set; }
    public string Term { get; set; }
    public string Language { get; set; }
    public bool PreferredTerm  { get; set; }
    public TermStatus TermStatus { get; set; }
    public Authority Use { get; set; }
    public List<Authority> UsedFor { get; set; }
    public List<Authority> Equivalent { get; set; }
    public List<Authority> Broader { get; set; }
    public List<Authority> Narrower { get; set; }
}

When the columns are created in the Authority table in the underlying SQL database, the column names for each of the List properties are Authority_ID, Authority_ID1, Authority_ID2 and Authority_ID3.

I would rather the column names to be 'UsedFor', 'Equivalent', 'Broader' and 'Narrower'. I have tried using the [Column("name")] attribute but it does not work. How can I do this in Code First?

Try this. I am not sure if the definitions of the foreign keys must be in the POCO class (you can try to omit them).

public class Authority
{
    [Key()]
    public long ID { get; set; }
    public string Term { get; set; }
    public string Language { get; set; }
    public bool PreferredTerm  { get; set; }
    public TermStatus TermStatus { get; set; }
    public Authority Use { get; set; }

    [Required]
    public long Authority_ID { get; set; }

    [Required]
    public long Authority_ID1 { get; set; }

    [Required]
    public long Authority_ID2 { get; set; }

    [Required]
    public long Authority_ID3 { get; set; }

    [ForeignKey("Authority_ID")]
    public ICollection<Authority> UsedFor { get; set; }

    [ForeignKey("Authority_ID1")]
    public ICollection<Authority> Equivalent { get; set; }

    [ForeignKey("Authority_ID2")]
    public ICollection<Authority> Broader { get; set; }

    [ForeignKey("Authority_ID3")]
    public ICollection<Authority> Narrower { get; set; }
}

You can use [InverseProperty("name")] for your list. After that, your column names will be "UsedFor_ID", "Equilavent_ID", etc in the database (not quite corresponding to your question, sorry!).

public class Authority
{
    [InverseProperty("UsedFor")]    
    public List<Authority> UsedFor { get; set; }
    [InverseProperty("Equivalent")]  
    public List<Authority> Equivalent { get; set; }    
}

See more at: https://msdn.microsoft.com/en-us/data/gg193958 http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx

Thanks for the suggestions. What worked was to use [ForeignKey] for the link and [Column] to rename the column, ie

    [Column("Use")]
    public long? UseID { get; set; }

    [ForeignKey("UseID")]
    public List<Authority> Use { get; set; }

However I have also made a critical mistake in the definition because even though I defined the column as a List, the code above ends up with a 1-to-0/1 key. What I really needed to do was to add in a child table to accept the many values.

My final code looks like this, and the underlying column names are readable instead of Authority_ID, Authority_ID1, Authority_ID2 and Authority_ID3:

public class Authority
{
    public long ID { get; set; }
    public string Term { get; set; }
    public string Language { get; set; }
    public bool PreferredTerm  { get; set; }
    public TermStatus TermStatus { get; set; }

    //Establish 1-to-0/1 self-referencing key
    [Column("Use")]
    public long? UseID { get; set; }

    [ForeignKey("UseID")]
    public List<Authority> Use { get; set; }

    //Establis 1-many foreign keys
    [ForeignKey("UsedFor")]
    public List<AuthorityList> UsedFor { get; set; }

    [ForeignKey("Equivalent")]
    public List<AuthorityList> Equivalent { get; set; }

    [ForeignKey("Broader")]
    public List<AuthorityList> Broader { get; set; }

    [ForeignKey("Narrower")]
    public List<AuthorityList> Narrower { get; set; }
}

public class AuthorityList
{
    public long ID { get; set; }
    public long AuthorityID { get; set; }
    public long? UsedFor { get; set; }
    public long? Equivalent { get; set; }
    public long? Broader { get; set; }
    public long? Narrower { get; set; }
}

In order to prevent cascading deletes getting in the way I have also added the following into my database context (affects entire DB not just these tables):

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

Updated answer :

Using the above gave me the underlying table structure I wanted but not the functionality I required since EF decided that under that configuration it was going to map a 1-1 relationship instead of 1-M. The answer lay in understanding how EntityFramework manages self-referencing Many-to-Many relationships. Even this can be configured multiple ways depending on whether you only want two 1-M relationships or more. I want six.

In the end, this configuration gave me the functionality I wanted, to the expense of having a less than ideal database structure.

public class Tag
{
    public int ID { get; set; }
    public string Term { get; set; }

    public virtual ICollection<Tag> Broader { get; set; }
    public virtual ICollection<Tag> Narrower { get; set; }
    public virtual ICollection<Tag> Equivalent { get; set; }
    public virtual ICollection<Tag> Related { get; set; }
    public virtual ICollection<Tag> Use { get; set; }
    public virtual ICollection<Tag> Usefor { get; set; }

    public Tag()
    {
        Broader = new HashSet<Tag>();
        Narrower = new HashSet<Tag>();
        Equivalent = new HashSet<Tag>();
        Related = new HashSet<Tag>();
        Use = new HashSet<Tag>();
        Usefor = new HashSet<Tag>();
    }
}

I also needed to add the following entries into the 'OnModelCreating' procedure of the database context:

    modelBuilder.Entity<Tag>()
        .HasMany(x => x.Broader)
        .WithMany()
        .Map(x => x.MapLeftKey("TagID").MapRightKey("BroaderID").ToTable("TagBroader"));

    modelBuilder.Entity<Tag>()
        .HasMany(x => x.Equivalent)
        .WithMany()
        .Map(x => x.MapLeftKey("TagID").MapRightKey("EquivalentID").ToTable("TagEquivalent"));

    modelBuilder.Entity<Tag>()
        .HasMany(x => x.Narrower)
        .WithMany()
        .Map(x => x.MapLeftKey("TagID").MapRightKey("NarrowerID").ToTable("TagNarrower"));

    modelBuilder.Entity<Tag>()
            .HasMany(x => x.Related)
            .WithMany()
            .Map(x => x.MapLeftKey("TagID").MapRightKey("RelatedID").ToTable("TagRelated"));

    modelBuilder.Entity<Tag>()
        .HasMany(x => x.Use)
        .WithMany()
        .Map(x => x.MapLeftKey("TagID").MapRightKey("UsedID").ToTable("TagUse"));

    modelBuilder.Entity<Tag>()
        .HasMany(x => x.Usedfor)
        .WithMany()
        .Map(x => x.MapLeftKey("TagID").MapRightKey("UsedforID").ToTable("TagUsedfor"));

To test, I used the following:

        //Broader/Narrower example
        var music = new Tag{ Term = "Music"};
        var jazz = new Tag{ Term = "Jazz Music" };
        var classical = new Tag{ Term = "Classical Music" };

        music.Narrower.Add(jazz);
        music.Narrower.Add(classical);
        jazz.Broader.Add(music);
        classical.Broader.Add(music);

        //Equivalent example
        var zucchini = new Tag{ Term = "Zucchini" };
        var courgette = new Tag{ Term = "Courgette" };

        zucchini.Equivalent.Add(courgette);
        courgette.Equivalent.Add(zucchini);

        context.Tags.Add(music);
        context.Tags.Add(jazz);
        context.Tags.Add(classical);
        context.Tags.Add(zucchini);
        context.Tags.Add(courgette);
        context.SaveChanges();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM