Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrating existing many-to-many relationships to new implicit many-to-many relationships #23963

Closed
adnan-kamili opened this issue Jan 25, 2021 · 10 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@adnan-kamili
Copy link

We have many existing many-to-many relationships wherein we have created the join table to map the entities as was needed before efcore 5.0

So we had three entities User, Tag and UserTag. We changed this to use the new EFCore 5.0 way, but the new migrations created drop the existing join table and tries to create a new join table, which will cause data loss and all the existing relationships will be lost. We also removed the join table entry from DBContext:

public DbSet<UserTag> UserTags { get; set; }

Here is the migration code generated:

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "UserTags");

            migrationBuilder.CreateTable(
                name: "UserTag",
                columns: table => new
                {
                    UsersId = table.Column<string>(type: "text", nullable: false),
                    TagsId = table.Column<string>(type: "text", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_UserTag", x => new { x.UsersId, x.TagsId });
                    table.ForeignKey(
                        name: "FK_UserTag_Users_LicensesId",
                        column: x => x.UsersId,
                        principalTable: "Users",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_UserTag_Tags_TagsId",
                        column: x => x.TagsId,
                        principalTable: "Tags",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_UserTag_TagsId",
                table: "UserTag",
                column: "TagsId");
        }

EF Core version: 5.0.1
Database provider: Microsoft.EntityFrameworkCore.Npgsql
Target framework: .NET 5.0

@anranruye
Copy link

anranruye commented Jan 25, 2021

Obvious, the implicit join table name is UserTag which is different from UserTags. You should use explicit configuration to rename the join table name to UserTags. Or you can first rename the DbSet name UserTags to UserTag, add a new migration, then remove the DbSet.

@ajcvickers
Copy link
Contributor

@adnan-kamili As @anangaur says, you will need to configure the many-to-many relationship to match your existing table and column names. See https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#many-to-many for more information.

@ajcvickers ajcvickers added the closed-no-further-action The issue is closed and no further action is planned. label Jan 25, 2021
@adnan-kamili
Copy link
Author

Thanks, renaming the DbSet before migrating to the new method worked. Creating and adding tags on User entity works too by just adding the tag to the user.Tags collection.

But when I remove a tag from user.Tags it doesn't work (tag is not removed from the user). The tag remains there only. Previously removing UserTag from user.UserTags join table worked. The following code (using UserTags instead of Tags) worked when join table was used:

user.Tags.RemoveAll(t => !requestModel.Tags.Contains(t.Name));
context.Entry(user);
await context.SaveChangesAsync();

@ajcvickers
Copy link
Contributor

@adnan-kamili Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@adnan-kamili
Copy link
Author

I am not sure whether I will be able to provide one, but one additional info that may help is that I eager-load the user and user.Tags as no-tracking entities. Then attach user and user.tags for tracking. Then update the entities. The removed entities from user.Tags are not removed on SaveAsync().

@ajcvickers
Copy link
Contributor

@adnan-kamili The documentation on how change tracking works might help you with this: https://docs.microsoft.com/en-us/ef/core/change-tracking/

@anranruye
Copy link

anranruye commented Jan 27, 2021

@adnan-kamili see #23787, this issue is already fixed by version 5.0.3 which is not released yet.

@adnan-kamili
Copy link
Author

@anranruye I could see this was some bug, thanks for the issue link. For now, I had to revert to using join tables.

One more issue which I faced is that when loading user as no-tracking entity (with tags eager-loader), any update to the user causes a crash unless I attach user and user.tags both (when using context.Entry(entity).State = EntityState.Unchanged; for attaching, as we don't want to unnecessarily attach all children).

@ajcvickers
Copy link
Contributor

@adnan-kamili While we try our best, we generally are not going to be able to give answers without seeing what your code is doing. Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@ajcvickers
Copy link
Contributor

EF Team Triage: Closing this issue as the requested additional details have not been provided and we have been unable to reproduce it.

BTW this is a canned response and may have info or details that do not directly apply to this particular issue. While we'd like to spend the time to uniquely address every incoming issue, we get a lot traffic on the EF projects and that is not practical. To ensure we maximize the time we have to work on fixing bugs, implementing new features, etc. we use canned responses for common triage decisions.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

3 participants