简体   繁体   中英

Many to Many relationship with Fluent API (existing database)

My application will have Users and Groups. A User can be a part of more than one Group . A Group can also have more than one User .

I have been using this as a reference. In particular, the part near the bottom labeled "Many Relationship". The difference between that example and my project is that my table already exists and I need to map my models to it. I suspect I should be able to remove my primary key from the Membership table and it might work (since it closer matches the example), but I would like to make sure before I mess with the database tables. It's also very likely that my Fluent is totally wrong.

EDIT

As I am further researching the answer to this question, I stumbled upon a few things that may enhance the quality of the answers provided. I want to note that it is required to be able to add a User , Usergroup , and Membership independently.

SQL

CREATE TABLE TestDB.[UserGroup]
(
    GroupID int identity not null,
    Name varchar(100) not null,
    Comment varchar(1000)

    PRIMARY KEY(GroupID)
)
CREATE TABLE TestDB.[User]
(
    SomeID int not null,
    FirstName varchar(100) not null,
    LastName varchar(100) not null,
    Middle varchar(1) not null,
    Email varchar(100) not null,
    Phone varchar(16) not null,
    Comment varchar(1000)

    PRIMARY KEY(SomeID)
)
CREATE TABLE TestDB.[Membership]
(
    MembershipID int identity not null,
    GroupID int not null,
    SomeID int not null

    PRIMARY KEY(MembershipID)
    FOREIGN KEY(GroupID) references TestDB.[UserGroup](GroupID),
    FOREIGN KEY(SomeID) references TestDB.[User](SomeID)
)

NOTE: SomeID is named as such because the ID is given via another system. The database will not automatically generate this particular ID. It is guaranteed to be unique.

Model

public class UserGroup
{ 
    public int GroupID { set; get; }
    public string Name { set; get; }
    public string Comment { set; get; }

    public virtual ICollection<User> Users { set; get; }
}
public class User
{
    public string SomeID { set; get; }
    public string FirstName { set; get; }
    public string LastName { set; get; }
    public string Email { set; get; }
    public string Phone { set; get; }
    public string Comment { set; get; }

    public virtual ICollection<UserGroup> UserGroups { set; get; }
}

Fluent Mapping

modelBuilder.Entity<User>().ToTable("User", "TestDB");
modelBuilder.Entity<User>().HasKey(r => new { r.SomeID });
modelBuilder.Entity<User>()
            .HasMany(r => r.UserGroups)
            .WithMany(r => r.Users)
            .Map(m =>
                {
                    m.MapLeftKey("SomeID");
                    m.MapRightKey("GroupID");
                    m.ToTable("Membership");
                });

modelBuilder.Entity<UserGroup>().ToTable("UserGroup", "TestDB");
modelBuilder.Entity<UserGroup>().HasKey(r => new { r.GroupID });

Current Issue Code has been updated to reflect what I currently have. I am able to query both Users and UserGroups independently, but it will not allow me to access the Navigation Properties .

I figured it out. My initial issues were quite silly and I was a lot closer than I had thought. One of those silly mistakes was actually mapping two entities to the same table which broke every call to that database.

The first thing you need to do is make sure all your Navigation properties are virtual.

public virtual ICollection<User> Users { set; get; }

From here you need to figure out the Fluent API mapping. I had initially forgotten to include the schema for ToTable , which was not allowing me to traverse from User to UserGroup . In an instance where you are not working on an existing database, the .Map block would not be required. A table would be generated automatically containing Keys SomeID and GroupID . The table name will be the result of the two child table names concatenated. Read more here . When working with an existing database, this will simply map that table it would have created to one you already did.

modelBuilder.Entity<User>()
        .HasMany(r => r.UserGroups)
        .WithMany(r => r.Users)
        .Map(m =>
            {
                m.MapLeftKey("SomeID");
                m.MapRightKey("GroupID");
                m.ToTable("Membership", "TempDB");
            });

Another Great Source

You dont need or want to have users in groups and groups in users. You should pick one or the other. IMHO I would drop List <user> from UserGroups object.

A collection of User objects can still be queried with Linq to generate a list of users who belong to a specific group.

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