1

Goal: apply data in the tables [Movies], [GenreMovie] and [Genres] in an efficent approach (high performance).

Problem: when you need to reuse the data that is already existing in the database by using Id but you get this error:

The instance of entity type 'Genre' cannot be tracked because another instance with the same key value for {'GenreId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

From this piece of code

_schoolContext.AddRange("

        else
        {
            var comedy = new Genre() { GenreId = genreAsDictionary["Comedy"] };
            var action = new Genre() { GenreId = genreAsDictionary["Action"] };

            // Bug here
            _schoolContext.AddRange(
                new Movie() { Name = "Test test", GenresGenres = new List<Genre>() { comedy, action } });

            _schoolContext.SaveChanges();
        }

I do not want another duplicate of same data that is already existing in the database.

How should I solve this situation?

Thank you!

Source code: https://www.learnentityframeworkcore5.com/whats-new-in-ef-core-5/many-to-many-relationship

using Microsoft.AspNetCore.Mvc;
using WebApplicationStudent.Database;

namespace WebApplicationStudent.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly ILogger<WeatherForecastController> _logger;
        private readonly SchoolContext _schoolContext;
        private readonly Repo _repo;

        public WeatherForecastController(ILogger<WeatherForecastController> logger, SchoolContext schoolContext, Repo repo)
        {
            _logger = logger;
            _schoolContext = schoolContext;
            _repo = repo;
        }

        [HttpGet(Name = "AddData")]
        public async Task<int> Get()
        {
            try
            {
                //_schoolContext.Database.EnsureCreated();

                var genreAsDictionary = await _repo.GetGenre();
                var movieAsDictionary = await _repo.GetMovie();

                if (!genreAsDictionary.Any() && !movieAsDictionary.Any())
                {
                    var comedy = new Genre() { GenreName = "Comedy" };
                    var action = new Genre() { GenreName = "Action" };
                    var horror = new Genre() { GenreName = "Horror" };
                    var scifi = new Genre() { GenreName = "Sci-fi" };

                    _schoolContext.AddRange(
                        new Movie() { Name = "Avengers", GenresGenres = new List<Genre>() { action, scifi } },
                        new Movie() { Name = "Ants", GenresGenres = new List<Genre>() { action, scifi } },
                        new Movie() { Name = "Satanic Panic", GenresGenres = new List<Genre>() { comedy, horror } });

                    _schoolContext.SaveChanges();
                }
                else
                {
                    var comedy = new Genre() { GenreId = genreAsDictionary["Comedy"] };
                    var action = new Genre() { GenreId = genreAsDictionary["Action"] };

                    // Bug here
                    _schoolContext.AddRange(
                        new Movie() { Name = "Test test", GenresGenres = new List<Genre>() { comedy, action } });

                    _schoolContext.SaveChanges();
                }
            }
            catch (Exception ex) 
            {
                int ff = 23;
            }

            return 1;
        }
    }
}

using Microsoft.EntityFrameworkCore;
using WebApplicationStudent.Controllers;
using WebApplicationStudent.Database;

namespace WebApplicationStudent
{
    public class Repo
    {
        private readonly ILogger<WeatherForecastController> _logger;
        private readonly SchoolContext _schoolContext;

        public Repo(ILogger<WeatherForecastController> logger, SchoolContext schoolContext)
        {
            _logger = logger;
            _schoolContext = schoolContext;
        }

        public async Task<Dictionary<string, int>> GetGenre()
        {
            return await _schoolContext.Genres.ToDictionaryAsync(a => a.GenreName ?? "", a => a.GenreId);
        }

        public async Task<Dictionary<string, int>> GetMovie()
        {
            return await _schoolContext.Movies.ToDictionaryAsync(a => a.Name ?? "", a => a.MovieId);
        }
    }
}

Scaffold-DbContext "Server=DESKTOP-TCK\MSSQLSERVER2022;Database=Testtest2;Integrated Security=true;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir "Database" -ContextDir "Database" -DataAnnotations -Context SchoolContext -NoOnConfiguring -WarningAction:SilentlyContinue -Force -Project WebApplicationStudent -StartupProject WebApplicationStudent -schema dbo

CREATE TABLE [dbo].[Movies] (
    [MovieId] INT            IDENTITY (1, 1) NOT NULL,
    [Name]    NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_Movies] PRIMARY KEY CLUSTERED ([MovieId] ASC)
);

CREATE TABLE [dbo].[Genres] (
    [GenreId]   INT            IDENTITY (1, 1) NOT NULL,
    [GenreName] NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_Genres] PRIMARY KEY CLUSTERED ([GenreId] ASC)
);

CREATE TABLE [dbo].[GenreMovie] (
    [GenresGenreId] INT NOT NULL,
    [MoviesMovieId] INT NOT NULL,
    CONSTRAINT [PK_GenreMovie] PRIMARY KEY CLUSTERED ([GenresGenreId] ASC, [MoviesMovieId] ASC),
    CONSTRAINT [FK_GenreMovie_Genres_GenresGenreId] FOREIGN KEY ([GenresGenreId]) REFERENCES [dbo].[Genres] ([GenreId]) ON DELETE CASCADE,
    CONSTRAINT [FK_GenreMovie_Movies_MoviesMovieId] FOREIGN KEY ([MoviesMovieId]) REFERENCES [dbo].[Movies] ([MovieId]) ON DELETE CASCADE
);

using Microsoft.EntityFrameworkCore;
using WebApplicationStudent;
using WebApplicationStudent.Database;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddTransient<Repo>();

builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer("Server=DESKTOP-TCK\\MSSQLSERVER2022;Database=Testtest2;Trusted_Connection=True;TrustServerCertificate=True;MultipleActiveResultSets=true;"));



var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();

app.MapControllers();

app.Run();
0

1 Answer 1

2

Your Repo class will load the Genre entities you have in your database into your EF context when you call the GetGenre() method. The loaded entities in the context could look like this:

Genre(GenreId=1, GenreName="Comedy")
Genre(GenreId=2, GenreName="Action")
Genre(GenreId=3, GenreName="Horror")
Genre(GenreId=4, GenreName="Sci-fi")

Inside the else block you create new Genre objects with the same ids:

var comedy = new Genre() { GenreId = genreAsDictionary["Comedy"] }; // id will be 1
var action = new Genre() { GenreId = genreAsDictionary["Action"] }; // id will be 2

and when you use AddRange(), Entity Framework will see that you want to add new Genre entities, because you have created new Genre objects. But Entity Framework will fail with that exception because you try to add a new entity with the same id which already exists in the context. You are operating on the same SchoolContext.

If you want to use existing entities from the database context, access them from the _schoolContext variable. You can use code like var existingComedy = _schoolContext.Genres.Single(it => it.GenreId == genreAsDictionary["Comedy"]);. Then you get the Genre instance from the EF context that is already loaded inside.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.