DDiVita
DDiVita

Reputation: 4265

Thoughts on how to navigate between many-to-many relations in Entity Framework 4.1

I am not a huge fan of having to place navigation properties on the objects that are shared by multiple entities in a model. Here is an example of what I am talking about:

    public abstract class SomeEntity
    {
       public Guid Id {get;set;         
       public virtual ICollection<Attachment> Attachements {get;set;}
    }

    public class User: SomeEntity
    {
         ...
    }

    public class Thing: SomeEntity
    {
         ...
    }

    public class Attachment
    {
       public Guid Id {get;set;}         
       ...
    }

I have tired to avoid having navigation properties on the Attachment entity for Users and Things. My thought behind that is to avoid situations where we are using Lazy Loading.

One thought was to have a ICollection<SomeEntity> Entites on an Attachemnt, but not sure that would work, because I have run into mapping issues with that before. Another thought I had was to manually navigate from Attachments to the entities, but that would mean having to write a method to pass back a collection of objects and figuring out their types.

I am having EF generate link tables like this:

HasMany(e => e.Attachments).WithMany().Map(m => { m.MapLeftKey("AttachmentId");
                                                                  m.MapRightKey("UserId");
                                                                  m.ToTable("User_Attachments");
            });

Looking at my Attachments table there is no FK back to any of the link tables. Which makes sense, because I am not navigating back to them.

Upvotes: 1

Views: 125

Answers (1)

Arthur Vickers
Arthur Vickers

Reputation: 7533

If you are worried about lazy loading, then you could make the navigation property not virtual so that it will never kick in lazy loading.

Or you could make the navigation property private and then access the contents in a controlled manner. Once you make the navigation property private you will need to use one of the tricks for mapping private properties using the Code First API. For simplicity I'm using the one that embeds the EntityConfiguration inside the entity:

public class Attachment
{
    public Guid Id { get; set; }

    private ICollection<User> Users { get; set; }

    public class AttachmentConfiguration : EntityTypeConfiguration<Attachment>
    {
        public AttachmentConfiguration()
        {
            HasMany(e => e.Users)
                .WithMany(e => e.Attachements)
                .Map(m =>
                     {
                         m.MapLeftKey("UserId");
                         m.MapRightKey("AttachmentId");
                         m.ToTable("User_Attachments");
                     });
        }
    }
} 

Add this configuration inside OnModelCreating:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new Attachment.AttachmentConfiguration());
}

You can then access Users from inside Attachment in any way you want or expose the users from methods, etc.

You could also use the DbContext change tracker API to access them from anywhere you have a context. For example:

var users = context.Entry(attachment).Collection<User>("Users").CurrentValue;

If you really want no navigation property at all, then you could drop down to ObjectContext and get the RelatedEnd for the collection, which will be an EntityCollection. This is pretty tricky code for a number of reasons, including needing to know the conceptual model names for the association and end names, which you could find by dumping the EDMX. I'd avoid this approach if at all possible.

Upvotes: 1

Related Questions