Isaiah Nelson
Isaiah Nelson

Reputation: 2490

Why isn't my property aware of changes made directly to the database

I have a simple View and ViewModel. When you click on the ComboBox of Technicians and select a Technician, the Tests for the SelectedTechnician are displayed in the ListView you see below. This works perfectly well.

Note Technician has a one to many relationship with Test and each is an entity (Code-First Entity Framework).

MainViewModel.xaml

<Grid>
    <StackPanel>
        <ComboBox ItemsSource="{Binding Technicians, Mode=TwoWay}"
                  DisplayMemberPath="FullName"
                  SelectedItem="{Binding SelectedTechnician, Mode=TwoWay}">
        </ComboBox>
        <ListView ItemsSource="{Binding SelectedTechnician.Tests}"
                  SelectedItem="{Binding SelectedTest}"
                  HorizontalAlignment="Left" Height="100" Margin="10,10,0,0" VerticalAlignment="Top" Width="271">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Id" Width="auto" DisplayMemberBinding="{Binding Id}"/>
                    <GridViewColumn Header="Test" Width="auto" DisplayMemberBinding="{Binding TestTypeName}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </StackPanel>
</Grid>

MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase
{
    private WpfTestBedDb db = new WpfTestBedDb();

    public MainWindowViewModel()
    {
        Technicians = new ObservableCollection<Technician>();
    }

    private ObservableCollection<Technician> _technicians;

    public ObservableCollection<Technician> Technicians
    {
        get { return _technicians; }
        set
        {
            _technicians = value;
            OnPropertyChanged("Technicians");
        }
    }

    private Technician _selectedTechnician;

    public Technician SelectedTechnician
    {
        get { return _selectedTechnician; }
        set
        {
            if (_selectedTechnician != value)
            {
                _selectedTechnician = value;
                OnPropertyChanged("SelectedTechnician");
            }
        }
    }

    public void LoadTechnicians()
    {
        var query = from tech in db.Technicians
                    select tech;
        foreach (var technician in query)
        {
            Technicians.Add(technician);
        }
    }
}

Incidentally, to test out the capabilities of DataBinding on the ObservableCollection<Technician> Technicians;, as you see in the ViewModel above, I went into the database through SSMS and added a new Technician WHILE the app was still running. Once my addition had been made, I went to the app and clicked on my ComboBox of Technicians only to find that the list/UI hadn't been updated. And yet, I would have thought that because I had made the property Technicians an ObservableCollection, and one that raises the OnPropertyChanged("Technicians");, that the UI would have updated.

I did this experiment to simulate two of the same apps running at the same time, each reading and writing to the same database.

First Question: Why isn't the UI updating despite having my ObservableCollection raise OnPropertyChanged? That is, why isn't it aware of the changes made directly to the database (outside the app)?

Second Question: What do I need to add so that the collection of Technicians is constantly aware of DB changes? (I thought that is what ObservableCollection was for)

Upvotes: 1

Views: 138

Answers (2)

shamp00
shamp00

Reputation: 11326

In addition to Marc Gravell's answer, and in answer to your second question, you might like to check out the SqlDependency class.

Quoting from the documentation:

The SqlDependency object represents a query notification dependency between an application and an instance of SQL Server. An application can create a SqlDependency object and register to receive notifications via the OnChangeEventHandler event handler.

Upvotes: 2

Marc Gravell
Marc Gravell

Reputation: 1063864

Short version: most data access code is a disconnected model, not a live/connected model.

The "observable" here means "reacts to other changes in that layer", i.e. other .NET code manipulating the object model. Using ObservableCollection<T> doesn't make a giant hand reach out over the network from the database to tweak the bits.

Making instantly change-aware systems is more complicated than that; frankly I would suggest looking at something like pub/sub if you genuinely need instant updates. Otherwise, perhaps consider polling occasionally.

Additionally, note that if you do want to do polling, you can't just re-fetch the object via:

var stateNow = db.Technicians.Single(x => x.Id == myId);

because that might just hand you back the same object you already have; a lot of ORMs have an identity cache built in. To actually re-fetch the object you will need a second db-context instance.

Upvotes: 3

Related Questions