Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF core many to many configuration not working with Fluent API

I am having trouble with understanding and implementing a many to many repationship using the FLuent API and EF Core.

I have looked at this question and set up my relationship exactly as that but I am getting the following error:

Error CS1061 'CollectionNavigationBuilder' does not contain a definition for 'WithMany' and no extension method 'WithMany' accepting a first argument of type 'CollectionNavigationBuilder' could be found (are you missing a using directive or an assembly reference?)

This is my intention. I have a client who has many jobs. I should be able to get all the jobs linked to that client. EF should create the join table in the background...

Here are my classes:

public class Client : IEntityBase
{
    public int Id { get; set; }

    public int? JobId { get; set; }
    public ICollection<Job> Jobs { get; set; }
}

public class Job : IEntityBase
{
    public int Id { get; set; }
}

//my interface
public interface IEntityBase
{
    int Id { get; set; }
}

EDIT Here is the Fluent API I tried and where I get the error on the ".withMany"

        modelBuilder.Entity<Client>()
            .HasMany(p => p.Jobs)
            .WithMany(p => p.clients)
            .Map(m =>
            {
                m.MapLeftKey("ClientId");
                m.MapRightKey("JobId");
                m.ToTable("ClientJob");
            });

I am using a generic repository pattern as per Chris Sakell's blog. Here is the code for retrieving clients:

        IEnumerable<Client> _clients = _clientRepository
           .AllIncluding(s => s.Creator, s => s.Jobs, s => s.State)
           .OrderBy(s => s.Id)
           .Skip((currentPage - 1) * currentPageSize)
           .Take(currentPageSize)
           .ToList();

and I am using the generic code as per:

    public virtual IEnumerable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
    {
        IQueryable<T> query = _context.Set<T>();
        foreach (var includeProperty in includeProperties)
        {
            query = query.Include(includeProperty);
        }
        return query.AsEnumerable();
    }

How do I configure this so I can retrieve the jobs as well using the includeproperty as per the Allincluding statement above?

like image 525
si2030 Avatar asked Feb 20 '17 06:02

si2030


People also ask

Does EF core support Fluent API?

EF Fluent API is based on a Fluent API design pattern (a.k.a Fluent Interface) where the result is formulated by method chaining. In Entity Framework Core, the ModelBuilder class acts as a Fluent API.

How do you create a many-to-many relationship in EF core?

Many-to-many relationships require a collection navigation property on both sides. They will be discovered by convention like other types of relationships. The way this relationship is implemented in the database is by a join table that contains foreign keys to both Post and Tag .

Is EF core faster than EF6?

Entity Framework (EF) Core, Microsoft's object-to-database mapper library for . NET Framework, brings performance improvements for data updates in version 7, Microsoft claims. The performance of SaveChanges method in EF7 is up to 74% faster than in EF6, in some scenarios.


2 Answers

The Fluent API example you are trying to implement comes from EF 6. Many-to-Many relationship configuration is a little different in EF Core. For a start, you need to include an entity to represent the join/bridging table:

public class ClientsJobs
{
    public int ClientId { get; set; }
    public int JobId { get; set; }
    public Client Client { get; set; }
    public Job Job { get; set; }
}

Then you configure it like this in the OnModelCreating method:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ClientsJobs>()
        .HasKey(x => new { x.ClientId, x.JobId });

    modelBuilder.Entity<ClientsJobs>()
        .HasOne(x => x.Client)
        .WithMany(y => y.Jobs)
        .HasForeignKey(y => y.JobId);

    modelBuilder.Entity<ClientsJobs>()
        .HasOne(x => x.Job)
        .WithMany(y => y.Clients)
        .HasForeignKey(y => y.ClientId);
}

See more about it here: http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

Note: you do need to include navigational properties for both ends of the relationship in the related classes, so you need to add a Clients property to your Job entity.

like image 184
Mike Brind Avatar answered Oct 11 '22 12:10

Mike Brind


For EF Core 5.0 and up, you may (finally) use direct relationships for many-to-many:

modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity(j => j.ToTable("PostTags"));

Source: Relationships - EF Core | Microsoft (many-to-many)

like image 20
Ross Brasseaux Avatar answered Oct 11 '22 12:10

Ross Brasseaux