Search code examples
c#mysqlasp.net-mvcentity-framework-core

.NET EntityFrameWork adds extra data


I am writing MVC Web API with .NET. When I add data to one of the models that reference each other, data is also added to the database of the other model. City:

public class City
 {
     public City()
     {
         Photos = new List<Photo>();
     }
     public int Id { get; set; }
     public int UserId { get; set; }
     public string Name { get; set; }
     public string? Description { get; set; }

     public List<Photo> Photos { get; set; }
     public User? User { get; set; }
 }

User:

public class User
{

    public User()
    {
        Cities = new List<City>();
    }
    public int Id { get; set; }
    public string? UserName { get; set; }
    public byte[]? PasswordHash { get; set; }
    public byte[]? PasswordSalt { get; set; }

    public List<City> Cities { get; set; }
}

Photo:

public class Photo
{
    public int Id { get; set; }
    public int CityId { get; set; }
    public string Url { get; set; }
    public string Description { get; set; }
    public DateTime DateAdded { get; set; }
    public bool IsMain { get; set; }
    public string PublicId { get; set; }

    public City City { get; set; }
}
DataContext : `public class DataContext : DbContext
{
    public DataContext(DbContextOptions<DataContext> options):base(options) 
    {
        
    }

    
    public DbSet<City> Cities { get; set; }
    public DbSet<Photo> Photos { get; set; }
    public DbSet<User> Users { get; set; }
}

And AppRepository :

public class AppRepository : IAppRepository
{
    private DataContext _context;

    public AppRepository(DataContext context)
    {
        _context = context;
    }
    public void Add<T>(T entity) where T : class
    {
        _context.Add(entity);
    }
  

    public void Delete<T>(T entity) where T : class
    {
        _context.Remove(entity);
    }

    public List<City> GetCities()
    {
        var cities = _context.Cities.Include(c=>c.Photos).ToList();
        return cities;
    }

    public City GetCityById(int cityId)
    {
        var city = _context.Cities.Include(c=>c.Photos).FirstOrDefault(c=>c.Id == cityId);
        return city;
    }

    public Photo GetPhoto(int id)
    {
        var photo = _context.Photos.FirstOrDefault(p=>p.Id == id);
        return photo;
    }

    public List<Photo> GetPhotosByCity(int cityId)
    {
        var photos = _context.Photos.Where(p => p.CityId == cityId).ToList();
        return photos;
    }

    public bool SaveAll()
    {
        return _context.SaveChanges() > 0;
    }
}

My CitiesController:

[Route("api/[controller]")]
 [ApiController]
 public class CitiesController : ControllerBase
 {
     private IAppRepository _appRepository;
     private IMapper _mapper;

     public CitiesController(IAppRepository appRepository,IMapper mapper)
     {
         _appRepository = appRepository;
         _mapper = mapper;
     }
     [HttpGet]
     public ActionResult GetCities()
     {
         var cities = _appRepository.GetCities();
         var citiesToReturn = _mapper.Map<List<CityForListDto>>(cities);
         return Ok(citiesToReturn);
     }

     [HttpPost]
     [Route("add")]
     public ActionResult Add([FromBody]City city)
     {
         _appRepository.Add(city);
         _appRepository.SaveAll();
         return Ok(city);
     }

Whenever I post to api/add and add a city, the user information I need to send is also saved user table in the database.Why this is happens ??

I used postman when adding the city and send data in body json format. I thought maybe it wouldn't be added if I didn't send the user information, but I got the error that the fields were required. I kept getting this error even though these fields are ALLOW NULLS in the database.Am I misunderstanding the relationships between modules?


Solution

  • With Entity Framework, if you save an entity that has other entities attached, those entities will also be saved.

    So if you pass up a new City object and this has a User object attached, then the database will try to update/create a new User also.

    Entity Framework is quite flexible in the ways it lets you edit and persist changes to entities. I would advise you read more here: https://learn.microsoft.com/en-us/ef/core/saving/related-data

    It sounds like your issues might be to do with how you are attaching the user and city for the view. If you don't want the user to be added with the city, then consider setting City.User to null before you post to the controller. You might also consider making City.UserId a nullable int (int?) as well to match the optional nature of the relationship.

    I would advise against generalised repository methods like Add and Remove, and instead make specific methods which deal with saving and deleting different entities. Entity Framework is itself a repository layer, and there is not much point in adding another on top of it. Instead consider using a service architecture, and perhaps defining view models which you can use for responses and requests. This way you can avoid mixing your database access logic and the manipulations you are doing in the view layer.