digipolisantwerp / dataaccess_aspnetcore_deprecated

Generic repository/unit of work framework for ASP.NET Core with Entity Framework.
Other
140 stars 45 forks source link

One to many update #43

Open Saathveegan opened 7 years ago

Saathveegan commented 7 years ago

I am having POCO classes as follows.

`public partial class Account : EntityBase
{
    public Account()
    {
        this.Discounts = new List<Discount>();          
    }

    public string AccountNo { get; set; }
    public string Name { get; set; }
    public IList<Discount> Discounts { get; set; }
}`

`public partial class Wholesaler : EntityBase
{
    public Wholesaler()
    {          
        this.Discounts = new List<Discount>();           
    }

    public string Name { get; set; }     
    public bool HasDiscount { get; set; }
    public IList<Discount> Discounts { get; set; }
}

`public partial class Discount 
{
    public int WholesalerRef { get; set; }
    public int AccountRef { get; set; }
    public decimal DiscountValue { get; set; }     
    public virtual Wholesaler Wholesaler { get; set; }       
    public virtual Account Account { get; set; }
}`

I have created the Discount class with the key of AccountRef & WholesalerRef . Is it OK to create a class without inheriting "EntityBase"?

       `entity.HasKey(e => new { e.AccountRef, e.WholesalerRef });

        entity.HasOne(e => e.Account)
            .WithMany(e => e.Discounts)
            .HasForeignKey(e => e.AccountRef)
            .HasConstraintName("FK_Discount_Account");

        entity.HasOne(e => e.Wholesaler)
            .WithMany(e => e.Discounts)
            .HasForeignKey(e => e.WholesalerRef)
            .HasConstraintName("FK_Discount_Wholesaler");`

When I insert a record it is working fine but when update I am getting an error.

System.InvalidOperationException occurred HResult=0x80131509 Message=The instance of entity type 'Discount' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context. Source= StackTrace: at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap1.Add(TKey key, InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, Func2 handleNode) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.NavigationCollectionChanged(InternalEntityEntry entry, INavigation navigation, IEnumerable1 added, IEnumerable1 removed) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.NavigationCollectionChanged(InternalEntityEntry entry, INavigation navigation, IEnumerable1 added, IEnumerable1 removed) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigation navigation) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager) at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess) at DataAccess.Uow.UnitOfWorkBase`1.SaveChanges() in

I am updating as below.

 `var accountDb = _repository.Get(account.Id, query => query.Include(b => b.Discounts));           
  destination.Discounts.Clear();
  accountDb .Name = source.Name;
  accountDb .AccountNo = source.AccountNo;
  destination.Discounts. = source.Discounts;  

_repository.Update(accountDb);
_unitOfWork.SaveChanges();`

Could you please let me know there is any workaround to tackle this issue using your repository pattern?

Regards, Saath

StevenVandenBroeck commented 7 years ago

Hi @Saathveegan,

you can use entities without inheriting them from EntityBase as long as you don't need our generic repositories for them. Could you clarify your update example because I can't determine where 'destination' and 'source' come from ?

Saathveegan commented 7 years ago

Hi,

Thank you for you reply.

Sorry I forgot to rename. it is suppose to be as follwos.

`public void UpdateAccount(AccountDto account) { var accountDb = _repository.Get(account.Id, query => query.Include(b => b.Discounts));

accountDb.Discounts.Clear();

accountDb .Name = account.Name; accountDb .AccountNo = account.AccountNo; accountDb.Discounts. = GetDiscounts(account)

_repository.Update(accountDb); _unitOfWork.SaveChanges();

}`

private IList<Discount> GetDiscounts(AccountDto account) { // Code will be here creating a list of discounts and return. }

I usually clear the list object and add it again. Seems like it is not working in EF core. May be I am doing wrong. Inserting was working fine, updating was the issue.

I do same as Stritof's answer at Stackoverflow (https://stackoverflow.com/questions/4253165/insert-update-many-to-many-entity-framework-how-do-i-do-it).

Regards, Saath

StevenVandenBroeck commented 7 years ago

I think that the problem is that you overwrite the existing list with a new list in accountDb.Discounts. Try adding the new elements to the existing list like this : accountDb.Discounts.Add(...) for each element that comes from GetDiscounts() or accountDb.Discounts.AddRange() to add the whole list in one call (not sure if that last method is supported by EF Core, you'll have to test it).

Saathveegan commented 7 years ago

Thank you for your reply. I will try and let you know...

Regards, Saath