Search code examples
c#wpfmvvmdapper

Dapper not updating records in MySQL with one to one relationship


Goal

I intend to update records from WPF application to MySQL.

The is the loaded data:

enter image description here

User is able to edit the data:

enter image description here

On button click, the data should update:

enter image description here

enter image description here

It detects the changes from the UI and shows updated records.

Problem

When looking in my database, I see the column Name has changed but PositionId remained the same:

enter image description here

Why is PositionId not updating? And how can I update it?

Code

Models

public class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public Position Position { get; set; }
}

public class Position
{
    public int PositionId { get; set; }
    public string PositionTitle { get; set; }

    public override bool Equals(object obj)
    {
        return obj is Position p && PositionId == p.PositionId;
    }

    public override int GetHashCode() => PositionId.GetHashCode();
}

View Model

public class MainViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Person> people;
    public ObservableCollection<Person> People
    {
        get { return people; }
        set
        {
            people = value;
            OnPropertyChanged();
        }
    }

    private ObservableCollection<Position> _positions;
    public ObservableCollection<Position> Positions
    {
        get { return _positions; }
        set
        {
            _positions = value;
            OnPropertyChanged();
        }
    }


    private static string connString = "..Connection String Goes here";
    private List<Person> LoadPersonData()
    {
        string query = "SELECT PersonId, Name, b.PositionId, b.PositionTitle FROM Person a JOIN Position b ON a.PositionId = b.PositionId";
        using (MySqlConnection conn = new MySqlConnection(connString))
        {
            var details = conn.Query<Person, Position, Person>(query, (person, position) =>
            {
                person.Position = position;

                return person;
            }, splitOn: "PositionId").ToList();

            return details;
        }
    }

    private List<Position> LoadPositionsData()
    {
        string query = "SELECT PositionId, PositionTitle FROM Position";
        using (MySqlConnection conn = new MySqlConnection(connString))
        {
            var details = conn.Query<Position>(query).ToList();

            return details;
        }
    }

    public MainViewModel()
    {
        People = new ObservableCollection<Person>();
        Positions = new ObservableCollection<Position>();

        // Add each record to property named People
        LoadPersonData().ForEach(record => People.Add(record));

        // Add each record to peroperty named Positions
        LoadPositionsData().ForEach(record => Positions.Add(record));

        Command = new RelayCommand(param => EditData());
    }

    public ICommand Command { get; }

    private void EditData()
    {

        // Update records in MySQL using dapper
        DapperPlusManager.Entity<Person>().Table("Person").Identity(x => x.PersonId);
        DapperPlusManager.Entity<Position>().Table("Position").Identity(x => x.PositionId);

        using(MySqlConnection conn = new MySqlConnection(connString))
        {
            conn.BulkUpdate(People, x => x.Position);
        }
    }


    #region Prop Changed
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

View

<StackPanel>
    <DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
            <DataGridTemplateColumn Header="Position Title">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox ItemsSource="{Binding Path=DataContext.Positions, 
                                                RelativeSource={RelativeSource AncestorType=DataGrid}}"
                                                DisplayMemberPath="PositionTitle"
                                                SelectedValue="{Binding Path=Position, UpdateSourceTrigger=PropertyChanged}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
    <Button Content="Save new data" Command="{Binding Command}" />
</StackPanel>

Solution

  • I think I've got it. What I noticed is in the documentation example:

    This loop

    enter image description here

    So I changed my EditData method to the following code:

    private void EditData()
    {
    
        foreach(var item in People)
        {
            item.PositionId = item.Position.PositionId;
        }
    
        // Update records in MySQL using dapper
        DapperPlusManager.Entity<Person>().Table("Person").Identity(x => x.PersonId);
        DapperPlusManager.Entity<Position>().Table("Position").Identity(x => x.PositionId);
    
        using(MySqlConnection conn = new MySqlConnection(connString))
        {
            conn.BulkUpdate(People, x => x.Position);
        }
    }
    

    and also added a Property in the Person model

    public int PositionId { get; set; }