Reputation: 261
I am new to Entity Framework and I'm trying to learn it.
I was trying to modify an exercise found on the official documentation: I would like to have a list of fathers and a list of sons. Every son must have a father selected from a combobox menu.
Now I can do that but, if I add a father, I don't see it in the father list nor in the combobox. If I add a son I don't see the son in the son's list.
If I close and reopen the program I correctly see fathers and sons previously added.
How I can automatically upgrade data in combobox and in the listview?
I don't like to call a function to refresh, I would like to automatically make the refresh when changing something in the database.
My project is made of 3 files:
MainWindow.xaml
<Window x:Class="EF7Fam.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EF7Fam"
mc:Ignorable="d"
Loaded="Page_Loaded"
Title="MainWindow" Height="350" Width="550">
<Grid>
<StackPanel Orientation="Horizontal">
<StackPanel Width="263" Margin="3 0 3 0">
<TextBox Name="NewFT"></TextBox>
<Button Click="Add_Click">Add</Button>
<ListView Name="Fathers">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<StackPanel Width="263" Margin="3 0 3 0">
<TextBox Name="NewSN"></TextBox>
<ComboBox Name="FT" DisplayMemberPath="Name" SelectedValuePath="FTId"/>
<Button Click="Add_SN_Click">Add</Button>
<ListView Name="Sons">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.Data.Entity;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace EF7Fam
{
/// <summary>
/// Logica di interazione per MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
using (var db = new Family())
{
db.Database.Migrate();
}
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
using (var db = new Family())
{
Fathers.ItemsSource = db.Fathers.ToList();
Sons.ItemsSource = db.Sons.ToList();
FT.ItemsSource = db.Fathers.ToList();
}
}
private void Add_Click(object sender, RoutedEventArgs e)
{
using (var db = new Family())
{
var ft = new Father { Name = NewFT.Text };
db.Fathers.Add(ft);
db.SaveChanges();
}
}
private void Add_SN_Click(object sender, RoutedEventArgs e)
{
using (var db = new Family())
{
var sn = new Son { Name = NewSN.Text, FTId = Int32.Parse(FT.SelectedValue.ToString()) };
db.Sons.Add(sn);
db.SaveChanges();
}
}
}
}
Model.cs
using System;
using Microsoft.Data.Entity;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Runtime.CompilerServices;
namespace EF7Fam
{
public class Family : DbContext
{
public DbSet<Father> Fathers { get; set; }
public DbSet<Son> Sons { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Filename=Family.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Father>()
.Property(b => b.Name)
.IsRequired();
}
}
public class Father : INotifyPropertyChanged
{
[Key]
public int FTId { get; set; }
public string Name { get; set; }
public List<Son> Sons { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Son : INotifyPropertyChanged
{
[Key]
public int SNId { get; set; }
public string Name { get; set; }
public int FTId { get; set; }
public Father Father { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I looked many times on this site and in the Web, but without finding a solution.
I will be very grateful if someone can help me to let me know what I doing wrong,
Thanks, bye,
Enrico
Upvotes: 3
Views: 1723
Reputation: 39366
To bound your controls to the DbSets
you need to do this:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
using (var db = new Family())
{
db.Fathers.Load();
Fathers.ItemsSource = db.Fathers.Local;
db.Sons.Load();
Sons.ItemsSource = db.Sons.Local;
FT.ItemsSource = db.Fathers.Local;
}
}
Calling Load
method you are going to load the existing objects into memory and DbSet<TEntity>.Local
property will give you an ObservableCollection<TEntity>
that contains all Unchanged
, Modified
and Added
objects that are currently tracked by the DbContext
for the given DbSet
. Adding or Removing from the ObservableCollection
will also perform the corresponding Add
/Remove
on the DbSet
.
This way if you need to save changes after perform all the operations that you need in your view, you can define a command or override the click event of a button in your view and call the SaveChanges
method of your context:
private void SaveChanges_Click(object sender, RoutedEventArgs e)
{
using (var db = new Family())
{
db.SaveChanges();
}
}
Now I saw that you are using EF7, well the true is I'm not aware about all the changes in this version, but I can tell you that there are a lot, maybe this property is not still implemented. I found a question asking about that and is not answered yet.
Digging more in documentation I think I found a solution but the true is I don't like it, but it could work until the Load
property shows up. According to the EF documentation you can do this:
private void Add_Click(object sender, RoutedEventArgs e)
{
using (var db = new Family())
{
var ft = new Father { Name = NewFT.Text };
db.Fathers.Add(ft);
db.SaveChanges();
Fathers.ItemsSource = db.Fathers.ToList();
}
}
If I find a better solution I'll let you know.
Upvotes: 3