I recently created a Web API project in .NET Core 3.1 and I implemented Entity Framework Core using code first approach. Each of my models refer to others like this:
public class Condominium
{
public int Id { get; set; }
public Address Address { get; set; }
public int Levels { get; set; }
public string Description { get; set; }
public CondominiumType CondominiumType { get; set; }
}
As you can see in this example I am referring to Address and to CondominiumType without using reference Id. Entity Framework will create the corresponding foreign keys when the database will be migrated.
This works perfectly fine when I have to add a new Condominium with a new Address but i am facing an issue when I want to add an existing Address or Condominium Type.
When I pass an object to the context with an Id that has already been added to the database the program throws an exception because i'm violating the UNIQUE constraint of the table. I know that I can fix this problem by adding reference keys writing something like this
public class Condominium
{
public int Id { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
public int Levels { get; set; }
public string Description { get; set; }
public int CondominiumTypeId { get; set; }
public CondominiumType CondominiumType { get; set; }
}
BUT I'm wondering which will be the proper way to resolve cases like this. ¿Is It Possible to add existing objects without rewriting my classes?
In the business Layer i'm passing the data throw an UnitOfWork Patter:
public async Task<bool> AddCondominium(int AgencyId,CondominiumDTO newCondominium)
{
var condominium = _mapper.Map<Condominium>(newCondominium);
condominium.ConsortiumAdministrationAgency = new ConsortiumAdministrationAgency() { Id = AgencyId };
_unitOfWork.Addresses.Add(condominium.Address);
_unitOfWork.Condominiums.Add(condominium);
return await _unitOfWork.CompleteAsync();
}
EntityFramework has to know if the object you have added to the context should be a new entity in the database or represent an already existing entity (with an Id). When you use the Add()
method you are saying that this object is for a new row/entity. But this means that it tries to add the new entity with the values in the object. When it has the Id
property set (which is the primary key) it tries to add the new row with that id, which will fail with a duplicate key constraint violation.
There are several solutions. You can get the object you want to reference from the context you are using. When you use something like
Address address = _context.Addresses.Single(it => it.Id == someId);
you can use the address
reference everywhere in your other entity classes to reference the existing entity from the database. This way, the object in address
is in the state of "tracked" or "attached" to the context of EntityFramework. The EntityFramework will not try to add it as a new row when you run SaveChanges()
but rather send UPDATE
queries when you change a value (if any).
The other way is to attach the address with the Attach()
method instead of Add()
to the context. That way the context of EntityFramework put the object in the "tracked" or "attached" state and will not try to add it as a new row when you run SaveChanges()
and assumes it is a entity from the database without running a SELECT
query from the database.