zzzprojects / EntityFramework-Effort

Entity Framework Effort is a powerful tool that enables a convenient way to create automated tests for Entity Framework based applications.
https://entityframework-effort.net/
MIT License
431 stars 99 forks source link

Change Tracking - Single property update of entity seems broken #206

Closed TobiasBreuer closed 4 years ago

TobiasBreuer commented 4 years ago

Description

In our code we have several cases where we only update specific properties of an entity. This is due to performance reasons and avoiding unnecessary fetching and updating of column values of an entity which are not affected.

In principal updates might look like this:

// fetching entity (mostly with some and not all of its properties!) and changing one of it's properties
var entity = context.Entities.AsNoTracking().FirstOrDefault(...);
entity.SomeProperty = "new Value";

// creating a new context (in this case with the Effort created DB connection as parameter)
// And updating just the specific property
using (var updateContext = new DBContext(effortConnection))
{
    updateContext.Attach(entity);
    updateContext.Entry(entity).Property(nameof(EntityClass.SomeProperty)).IsModified = true;

    updateContext.SaveChanges();
}

// Testing result
var updatedEntity = context.Entities.AsNoTracking().FirstOrDefault(...);
Assert.That("new Value", Is.Equal.To(entity.SomeProperty));

The code to update specific properties works fine in production environment. But when using Effort in Integration Tests scenario, this code won't update the respective entity in the database.

When marking the entire entity as changed updateContext.Entry(entity).State = EntityState.Modified; however, the call to SaveChanges() will update the entity as expected. This is however a no-go solution as in this case, all properties of the entity will be updated.

Further technical details

JonathanMagnan commented 4 years ago

Hello @TobiasBreuer ,

Thank you for reporting,

We will look if something can be done.

Best Regards,

Jon


Performance Libraries context.BulkInsert(list, options => options.BatchSize = 1000); Entity Framework ExtensionsEntity Framework ClassicBulk OperationsDapper Plus

Runtime Evaluation Eval.Execute("x + y", new {x = 1, y = 2}); // return 3 C# Eval FunctionSQL Eval Function

JonathanMagnan commented 4 years ago

Hello @TobiasBreuer ,

My developer tried it and told me that everything is working.

Could you try his code and let him know what he is missing to have the same behavior as you?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Effort.Lab.EF6
{
    public partial class Form_Request_trackingProperty : Form
    {
        public Form_Request_trackingProperty()
        {
            InitializeComponent();
            var connection = Effort.DbConnectionFactory.CreateTransient();

            // CLEAN
            using (var context = new EntityContext(connection))
            {
                context.EntitySimples.RemoveRange(context.EntitySimples);
                context.SaveChanges();
            }

            // SEED
            using (var context = new EntityContext(connection))
            {
                context.EntitySimples.Add(new EntitySimple { ColumnInt = 1 });
                context.EntitySimples.Add(new EntitySimple { ColumnInt = 2 });
                context.EntitySimples.Add(new EntitySimple { ColumnInt = 3 });
                context.SaveChanges();
            }

            EntitySimple entity = new EntitySimple();

            using (var context2 = new EntityContext(connection))
            {

                using (var context = new EntityContext(connection))
                {
                    entity = context.EntitySimples.AsNoTracking().First();
                }

                entity.ColumnInt = 50;

                // TEST
                using (var context = new EntityContext(connection))
                { 
                    context.EntitySimples.Attach(entity);
                    context.Entry(entity).Property(nameof(EntitySimple.ColumnInt)).IsModified = true;
                    context.SaveChanges(); 
                }  

                var check2 = context2.EntitySimples.AsNoTracking().ToList();

                // TEST
                using (var context = new EntityContext(connection))
                {
                    var check = context.EntitySimples.AsNoTracking().ToList();
                }
            }
        }

        public class EntityContext : DbContext
        {
            public EntityContext(DbConnection connection) : base(connection, true)
            {
            }

            public DbSet<EntitySimple> EntitySimples { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
            }
        }

        public class EntitySimple
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }
        }
    }
}
JonathanMagnan commented 4 years ago

Hello @TobiasBreuer ,

Since our last conversation, we haven't heard from you.

As mentioned in my last message, my developer tried it and haven't face the same behavior as you.

Did you get the time to try his code?

Looking forward to hearing from you,

Jon

TobiasBreuer commented 4 years ago

Hi @JonathanMagnan,

thanks for the quick response from your side. Unfortunately I didn't have the chance to respond any sooner.

Using the sample provided by your dev I'm also not able to reproduce the issue. I will setup a minimalistic example to reproduce the issue and come back to you once it's available. My current guess it that it only shows up in scenarios where entities are linked with foreign key constraints.

Will get back to you as soon as possible. Best regards,

Tobias

JonathanMagnan commented 4 years ago

Hello @TobiasBreuer ,

This issue will be closed since we haven't heard back from you.

However, we will reopen it as soon as you can provide an example.

Have a great day,

Jon