1

I have a simple One-To-Many relationship using EF6.

public abstract class AbstractModel
{
    [Key]
    public int Id { get; set; }
}

public class Parent : AbstractModel
{
    [Required]
    public string Name { get; set; } = string.Empty;

    public IEnumerable<Child> Childs { get; set; } = new List<Child>();
}

public class Child: AbstractModel
{
    [Required]
    public string Name { get; set; } = string.Empty;

    public Parent? Parent{ get; set; }

    [Required]
    public int ParentId { get; set; }
}

Additionally, I am using a generic repository/service approach to simplify DB access. All CRUD operations are working as expected, except for the update. I can update the Parents and Childs name without any problems, but if I add a Child to the Parents Childs list, changes are not written to the DB. The following code shows a unit test:

public async void Test_UpdateParent()
{
    var parentService = new ParentService(this.DbContextFactory);
    var parent = new Parent()
                 {
                     Name = "Test",
                     Childs = GenerateObjects<Child>(3, nameof(Child.Name)),
                 };

    await parentService.Add(parent);
    parent = await parentService.Get(parent.Id);
    Assert.NotNull(parent);

    parent.Name = "New Test";
    var childList = parent.Childs.ToList();
    childList.RemoveAt(2);
    parent.Childs = childList;

    Assert.Equal(true, await parentService.Update(parent));

    parent = await parentService.Get(parent.Id);
    Assert.NotNull(parent);

    Assert.Equal("New Test", parent.Name);
    Assert.Equal(2, parent.Childs);
    ValidateObjects(parent.Childs.ToList(), 2, nameof(Child.Name));
}

If I run this test, the Assert Assert.Equal(2, parent.Childs); fails, because there are still 3 children.

public virtual async Task<bool> Update(TModel entity)
{
    try
    {
        await using var context = await this.ContextFactory.CreateDbContextAsync();
        context.Entry(entity).State = EntityState.Modified;
        context.Update(entity);
        await context.SaveChangesAsync();
        return true;
    }
    catch (Exception)
    {
        await this.Refresh(entity);
        return false;
    }
}

I think the issue might be the fact that the context is created for each DB query. I have no way of testing this since it is required that the context is not disposed at any time. We ran into this issue, since our Blazor Server is fetching live data and needs to be up-to-date on each client at all times. This led to the problem that contexts, which were created within the constructor, went out of scope on the clients and made the application unusable.

Zumpel
  • 177
  • 9
  • `async void` for a Test seems spooky. Maybe not the direct problem but do make it an `async Task` – Henk Holterman May 05 '22 at 13:09
  • Removing the item from the "list" is different to calling your repository service to delete the item. – Brian Parker May 06 '22 at 03:38
  • Try to add in the Id of your `AbstractModel` this `[DatabaseGenerated(DatabaseGeneratedOption.Identity)]` – user13256346 May 06 '22 at 07:02
  • @BrianParker can you please elaborate on this behavior? This would mean that it would not be possible to implement a Generic Service/Repository, just because of the fact that you need to implement a solution to always keep track of the varying child elements and manually delete them through their corresponding service. I highly doubt your statement. – Zumpel May 06 '22 at 11:39
  • @user13256346 sadly this does not change anything, but thank you. – Zumpel May 06 '22 at 11:40
  • I believe your problem is that you are presenting EF with a detached class that it hasn't tracked. This is one of the issues in using a factory and unit of work approach. You're Update needs to get the EF object, update it and then call save. Here's a similar question and answers: https://stackoverflow.com/questions/27176014/how-to-add-update-child-entities-when-updating-a-parent-entity-in-ef?msclkid=11800f14ce9c11ecbc87d43bb33b8c64. One of the design issues with building your application logic into your data layer! – MrC aka Shaun Curtis May 08 '22 at 07:09

0 Answers0