I'm having an issue when ApplyChangesfor a given entity which has an unchanged FK related entity.
Consider the next entity class:
/// <summary>
/// Entity class representing data for table 'Product'.
/// </summary>
public partial class Product : SelfTrackedEntity, ISoftDeletable
{
#region Private fields
private bool _containsAllergens;
private bool _isContaminated;
private bool _isRecipeUpdated;
private int? _productTypeId;
#endregion
#region Properties
public bool ContainsAllergens
{
get => _containsAllergens;
set => SetProperty(ref _containsAllergens, value);
}
public bool IsContaminated
{
get => _isContaminated;
set => SetProperty(ref _isContaminated, value);
}
public bool IsRecipeUpdated
{
get => _isRecipeUpdated;
set => SetProperty(ref _isRecipeUpdated, value);
}
public int? ProductTypeId
{
get => _productTypeId;
set
{
SetProperty(ref _productTypeId, value);
this.SetRelationById<Product, int, int?>(ref _productTypeId, value, (e) => e._productType = null);
}
}
#endregion
#region Relationships
private ProductType _productType;
public virtual ProductType ProductType
{
get => _productType;
set => this.SetRelationByObject<Product, int, ProductType, int>(ref _productType, value, (e, id) => { e._productTypeId = id; SetProperty(ref e._productTypeId, id); });
}
#endregion
}
Some explanation about this product entity:
The SelfTrackedEntityimplements your ITrackable interface.
The SetPropertyhandles setting your ITrackable.ModifiedProperties collection.
When setting the ProductType property the ProductTypeId is set to the correct value. This is handled by the SetRelationByObject.
Vice versa: when setting the ProductTypeId we set the ProductTypeproperty to null.
Situation:
ProductType productType;
using (var unitOfWork = _unitOfWorkFactory.Create())
{
var productTypeRepository = unitOfWork.GetService<IProductTypeRepository>();
var productTypes = await productTypeRepository.GetAllAsync(q => q.SetTop(1));
productType = productTypes.First();
await unitOfWork.SaveChanges();
}
var p = new Product
{
ContainsAllergens = false,
IsContaminated = false,
IsRecipeUpdated = false,
ProductType = productType
};
using (var unitOfWork = _unitOfWorkFactory.Create())
{
var productRepository = unitOfWork.GetService<IProductRepository>();
productRepository.Add(p);
productRepository.ApplyChanges(p);
await unitOfWork.SaveChanges();
}
Some explanation about the code above:
The productType is fetched from the Database and detached due to out of scope of the unit of work in which we fetch it.
We create a new Product entity out of scope of any DbContext.
The ProductType property is set to the earlier fetched productType.
We add the product to the DbContext by use of the Add method on our IProductRepository.
We ApplyChanges which uses your ApplyChanges.
When SaveChanges we get a PK constraint exception because the productType is considered to be added again to the Database.
This is incorrect in my opinion because the TrackingState of this ProductType entity is still unchanged.
So why saving this in the Database at this point? Should the ApplyChanges not have to take the TrackingState into account to skip unchanged FK entities from being added again as Detached entities to the DbContext? Or at least should this not be an option to pass to the ApplyChanges to attach only the parent level entities and not the complete graph?
Hi Tony,
I'm having an issue when
ApplyChanges
for a given entity which has an unchanged FK related entity.Consider the next entity class:
Some explanation about this product entity:
SelfTrackedEntity
implements yourITrackable
interface.SetProperty
handles setting yourITrackable.ModifiedProperties
collection.ProductType
property theProductTypeId
is set to the correct value. This is handled by theSetRelationByObject
.ProductTypeId
we set theProductType
property to null.Situation:
Some explanation about the code above:
productType
is fetched from the Database and detached due to out of scope of the unit of work in which we fetch it.Product
entity out of scope of any DbContext.ProductType
property is set to the earlier fetchedproductType
.IProductRepository
.ApplyChanges
which uses your ApplyChanges.SaveChanges
we get a PK constraint exception because theproductType
is considered to be added again to the Database.This is incorrect in my opinion because the
TrackingState
of thisProductType
entity is still unchanged. So why saving this in the Database at this point? Should theApplyChanges
not have to take theTrackingState
into account to skip unchanged FK entities from being added again as Detached entities to the DbContext? Or at least should this not be an option to pass to theApplyChanges
to attach only the parent level entities and not the complete graph?