Home > Back-end >  Entity Framework 6 updates all records with data of the one getting updated
Entity Framework 6 updates all records with data of the one getting updated

Time:11-08

I am trying to implement entity framework 6 in my application, but I have a problem performing updates on the records.

If I have 2 records in the database, lets say:

Id Name Lastname
1 Jason Momoa
2 Aqua Man

Then I alter the one with id 1 from "Jason" to "Water" and call the UpdatePerson function, with a new Person object that has the same primary key.

The result will be:

Id Name Lastname
1 Water Momoa
2 Water Momoa

WHY should that be the result?? I was already looking around for a solution but could not find any clue. Anyone an idea what I am doing wrong?

As far as I understood the disconnected datacontext I used, can simply update a record with the knowledge of the primary key.

For reference the page for EF6

My code looks like this:

public class Person
{
    private int _id = -1;
    private string _name;
    private string _lastname;

    public int PersonId { get => _id; set => _id = value; }
    [Required]
    [MaxLength(255)]
    public string Name { get => _name; set => _name = value; }
    [Required]
    public string Lastname { get => _lastname; set => _lastname = value; }
}

DbContext:

public partial class Model1 : DbContext
{
    public Model1() : base("name=entity_test") { }

    public DbSet<Person> People { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Person>().MapToStoredProcedures();
    }     
}

public class PersonModel
{
    public ObservableCollection<Person> GetPeople()
    {
        using (Model1 context = new Model1())
        {
            var list = context.People.AsNoTracking().ToList();
            if (list == null)
                return null;
            return new ObservableCollection<Person>(list);
        }
    }

    public void AddPerson(Person person)
    {
        using (Model1 context = new Model1())
        {
            context.People.Add(person);
            context.SaveChanges();
        }
    }

    public void UpdatePerson(Person person)
    {
        using (Model1 context = new Model1())
        {
            context.Entry(person).State = EntityState.Modified;
            context.SaveChanges();
        }
    }
}

EDIT

Tables were not displayed nicely.

EDIT 2

Here the rest of the code and the output of context.Database.Log = s => Console.WriteLine(s);

Output:

`Person_Update`


-- PersonId: '1' (Type = Int32, IsNullable = false)

-- Name: 'Water' (Type = String, IsNullable = false, Size = 5)

-- Lastname: 'Momoa' (Type = String, IsNullable = false, Size = 5)

-- Executing at 29.10.2021 16:46:05  02:00

-- Completed in 198 ms with result: 2

Code:

public class NotifyBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();

    }
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        PersonModel model = new PersonModel();

        if (DataContext is ViewModel vm)
        {
            vm.AddModifyPerson();
        }
    }
}

public class ViewModel : NotifyBase
{
    public ViewModel()
    {
        MiniProfilerEF6.Initialize();
        model = new PersonModel();

        using (var db = new Model1())
        {
            // create if not exists
            if (db.Database.CreateIfNotExists())
            {
                Console.WriteLine();
            }
            People = model.GetPeople();
        }
    }

    private PersonModel model;

    private ObservableCollection<Person> people = new ObservableCollection<Person>();
    private Person currentPerson = new Person();

    public ObservableCollection<Person> People { get => people; set => SetProperty(ref people, value); }
    public Person CurrentPerson { get => currentPerson; set => SetProperty(ref currentPerson, value); }

    public void AddModifyPerson()
    {
        if (CurrentPerson.PersonId == -1)
        {
            model.AddPerson(CurrentPerson);
        }
        else
        {
            model.UpdatePerson(
                new Person()
                {
                    PersonId = CurrentPerson.PersonId,
                    Lastname = CurrentPerson.Lastname,
                    Name = CurrentPerson.Name,
                });
        }
        People = model.GetPeople();
    }
}

Edit 3

Code for from miniprofiler

    public void UpdatePerson(Person person)
    {
        var profiler = MiniProfiler.StartNew("My Profiler");
        using (MiniProfiler.Current.Step("Update_Sql"))
        {
            using (Model1 context = new Model1())
            {
                context.Entry(person).State = EntityState.Modified;
                context.SaveChanges();
            }
        }

        Console.WriteLine(MiniProfiler.Current.RenderPlainText());
    }

EDIT 4

output of an update call from mysql.general_log

command_type argument
Init DB entity_test
Query SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
Query BEGIN
Query CALL Person_Update(1, 'Jason1', 'Momoa')
Query COMMIT

It does look like the update procedure is not performing the where clause. If I run call Person_Update_Custom(1, 'test','tes') in the workbench all rows are updated. Here is the stored procedure:

    CREATE DEFINER=`root`@`localhost` PROCEDURE `Person_Update`(IN PersonId int,IN Name varchar(255) ,IN Lastname longtext)
    BEGIN 
    UPDATE `People` SET `Name`=Name, `Lastname`=Lastname WHERE `PersonId` = PersonId;
     END

CodePudding user response:

So after seeing in the miniprofiler (thanks Armando Bracho) and the mysql log (thanks Steve Py) that there was always one single sql query and Gert Arnold pointing out the procedure could be failing, I focused on the procedure.

It looks like, the procedure mixes the PersonId column name with the PersonId variable defined in the CREATE PROCEDURE-header.

So I've added some code to define the update procedure parameters by hand.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<Person>().MapToStoredProcedures(p => p.Update(pp => pp.HasName("Person_Update").Parameter(pm => pm.Name, "db_Name").Parameter(pm => pm.Lastname, "db_Lastname").Parameter(pm => pm.PersonId, "db_PersonId")));
}      

which changes the stored procedure to:

CREATE DEFINER=`root`@`localhost` PROCEDURE `Person_Update`(IN db_PersonId int,IN db_Name varchar(255) ,IN db_Lastname longtext)
BEGIN 
UPDATE `People` SET `Name`=db_Name, `Lastname`=db_Lastname WHERE `PersonId` = db_PersonId;
 END

and this finally works! One row affected.

EDIT

It's enough to change the MapToStoredProcedures to

modelBuilder.Entity<Person>().MapToStoredProcedures(p => p.Update(pp => pp.HasName("Person_Update").Parameter(pm => pm.PersonId, "db_PersonId")));

this ways one does not have define it for all properties.

  • Related