What approach should be taken to track changes in database operations in other windows?

fatih uyanık 100 Reputation points
2023-12-15T07:43:30.5366667+00:00

Hello

I continue to develop an application by injecting services with the MVVM pattern in a C# wpf project. I encountered a problem. I designed a DataAccess class to manage database operations (Insert, delete) with EF. A new record is added in a window where the data is listed, or after the selected record is updated in the new window, it is closed and the page is returned to the page where the listing was made. Here, while the listing window is closed, if there is a change, recording is made while the window is closed.

The problem is this: Every time I use a new link clause or a short-lived Context, I cannot achieve the result I want in the listing window. There are a few more problems like this.

In such a scenario I thought of injecting DataAccess class as Singleton. But I think I might have memory leaks and performance issues.

What is the approach that should be taken in this regard?

Thanks.

Entity Framework Core
Entity Framework Core
A lightweight, extensible, open-source, and cross-platform version of the Entity Framework data access technology.
751 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,784 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,015 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,326 Reputation points
    2023-12-16T21:28:16.05+00:00

    Hi,
    one approach you can see in following demo (one instance of dataAccess the whole time and edit window as dialog):

    XAML Mainwindow:

    <Window x:Class="WpfApp1.Window123"
            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:WpfApp123"
            mc:Ignorable="d"
            Title="fatih uyanık_231215" Height="250" Width="400">
      <Window.Resources>
        <local:ViewModelMain x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}" Margin="5">
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <DataGrid ItemsSource="{Binding View}" IsReadOnly="True" AutoGenerateColumns="False" Grid.ColumnSpan="2">
          <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding FirstName}"/>
            <DataGridTextColumn Binding="{Binding MiddleName}"/>
            <DataGridTextColumn Binding="{Binding LastName}"/>
            <DataGridTemplateColumn>
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <Button Content="Edit" Command="{Binding CmdEdit, Source={StaticResource vm}}" CommandParameter="{Binding}" Margin="2"/>
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <Button Content="Delete" Command="{Binding CmdDelete, Source={StaticResource vm}}" CommandParameter="{Binding}" Margin="2"/>
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
          </DataGrid.Columns>
        </DataGrid>
        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Grid.Row="1" Grid.ColumnSpan="2">
          <Button Content="New" Command="{Binding Cmd}" CommandParameter="New"  Margin="5"/>
          <Button Content="Refresh" Command="{Binding Cmd}" CommandParameter="Refresh" Margin="5"/>
        </StackPanel>
      </Grid>
    </Window>
    

    XAML DetailWindow:

    <Window x:Class="WpfApp1.Window123Detail"
            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:vm="clr-namespace:WpfApp123"
            mc:Ignorable="d"
            Title="Window123Sub" Height="200" Width="300"
            vm:ViewModelDetail.AttProp="True">
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid DataContext="{Binding View}">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
          </Grid.RowDefinitions>
          <Label Grid.Row="0" Grid.Column="0" Content="FirstName:" HorizontalAlignment="Right" Margin="5"/>
          <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding FirstName}" Margin="5"/>
          <Label Grid.Row="1" Grid.Column="0" Content="MiddleName:" HorizontalAlignment="Right" Margin="5"/>
          <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding MiddleName}" Margin="5"/>
          <Label Grid.Row="2" Grid.Column="0" Content="LastName:" HorizontalAlignment="Right" Margin="5"/>
          <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding LastName}" Margin="5"/>
        </Grid>
        <StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
          <Button Content="Save" Command="{Binding Cmd}" CommandParameter="Save" Margin="5"/>
          <Button Content="Delete" Command="{Binding Cmd}" CommandParameter="Delete" Margin="5"/>
          <Button Content="Cancel" Command="{Binding Cmd}" CommandParameter="Cancel" Margin="5"/>
        </StackPanel>
      </Grid>
    </Window>
    
    

    ViewModels:

    using System;
    using System.ComponentModel;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using WpfApp1;
    
    namespace WpfApp123
    {
    	public class ViewModelMain : INotifyPropertyChanged
    	{
    		public ViewModelMain() => refreshData();
    
    		private CollectionViewSource cvs = new CollectionViewSource(); // View source for displaying
    		public ICollectionView View { get => cvs.View; }
    
    		private void refreshData()
    		{
    			cvs.Source = (new DataAccess()).GetData();
    			OnPropertyChanged(nameof(View));
    		}
    
    		public ICommand Cmd { get => new RelayCommand(CmdExec); }
    		private void CmdExec(object obj)
    		{
    			switch (obj.ToString())
    			{
    				case "Refresh":
    					refreshData();
    					break;
    				case "New":
    					Window123Tab1 newItem = new Window123Tab1();
    					CmdEditExec(newItem);
    					break;
    				default:
    					break;
    			}
    		}
    		public ICommand CmdEdit { get => new RelayCommand(CmdEditExec); }
    		private void CmdEditExec(object obj)
    		{
    			Window123Detail wnd = new Window123Detail();
    			ViewModelDetail vm = new ViewModelDetail();
    			wnd.DataContext = vm;
    			DataAccess da = new DataAccess();
    			Window123Tab1 item = obj as Window123Tab1;
    			if (item.ID != 0) item = da.Tab1.SingleOrDefault(a => a.ID == item.ID);
    			if (item != null)
    			{
    				vm.View = item;
    				bool? toSave = wnd.ShowDialog();
    				if (toSave.Value)
    				{
    					if (item.ID == 0) da.AddData(item);
    					else da.UpdateData(item);
    				}
    				else
    				{
    					if (item.ID != 0) da.ReloadData(item);
    				}
    			}
    			refreshData();
    		}
    		public ICommand CmdDelete { get => new RelayCommand(CmdDeleteExec); }
    		private void CmdDeleteExec(object obj)
    		{
    			Window123Tab1 item = obj as Window123Tab1;
    			if (item != null) (new DataAccess()).DeleteData(item);
    			refreshData();
    		}
    
    		public event PropertyChangedEventHandler PropertyChanged;
    		private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    	}
    
    	public class ViewModelDetail
    	{
    		public object View { get; set; }
    		public ICommand Cmd { get => new RelayCommand(CmdExec); }
    
    		private void CmdExec(object obj)
    		{
    			switch (obj.ToString())
    			{
    				case "Save":
    					SubWindow.DialogResult = true;
    					SubWindow.Close();
    					break;
    				case "Delete":
    					(new DataAccess()).DeleteData((Window123Tab1)View);
    					SubWindow.DialogResult = true;
    					SubWindow.Close();
    					break;
    				case "Cancel":
    					SubWindow.DialogResult = false;
    					SubWindow.Close();
    					break;
    				default:
    					break;
    			}
    		}
    		public static readonly DependencyProperty AttPropProperty = DependencyProperty.Register("AttProp",
    	typeof(bool), typeof(Window), new UIPropertyMetadata(false, OnAttProp));
    		public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
    		public static void SetAttProp(DependencyObject obj, bool value) => obj.SetValue(AttPropProperty, value);
    		private static void OnAttProp(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    		{
    			var wnd = depObj as Window;
    			if (wnd == null) return;
    			if ((e.NewValue is bool) && (bool)(e.NewValue))
    				wnd.Loaded += Wnd_Loaded;
    		}
    		private static void Wnd_Loaded(object sender, RoutedEventArgs e)
    		{
    			Window wnd = sender as Window;
    			if (wnd == null) return;
    			ViewModelDetail dc = wnd.DataContext as ViewModelDetail;
    			if (dc == null) return;
    			dc.SubWindow = wnd;
    		}
    
    		Window SubWindow { get; set; }
    	}
    
    	public partial class DataAccess : DbContext
    	{
    		public DataAccess() : base("name=Database2Entities") { }
    
    		protected override void OnModelCreating(DbModelBuilder modelBuilder)
    		{
    			throw new UnintentionalCodeFirstException();
    		}
    		public virtual DbSet<Window123Tab1> Tab1 { get; set; }
    
    		internal object GetData()=> from item in Tab1.AsEnumerable() select item;
    		internal void AddData(Window123Tab1 item)
    		{
    			Tab1.Add(item);
    			this.SaveChanges();
    		}
    		internal void UpdateData(Window123Tab1 item) => this.SaveChanges();
    		internal void DeleteData(Window123Tab1 item)
    		{
    			Tab1.Remove(Tab1.Single(a => a.ID == item.ID));
    			this.SaveChanges();
    		}
    		internal void ReloadData(Window123Tab1 item)
    		{
    			if (this.Entry(item).State != EntityState.Detached)
    			{
    				this.Entry(item).State = EntityState.Unchanged;
    				this.SaveChanges();
    			}
    		}
    	}
    
    	public class RelayCommand : ICommand
    	{
    		private readonly Predicate<object> _canExecute;
    		private readonly Action<object> _action;
    		public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
    		public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
    		public void Execute(object o) => _action(o);
    		public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
    		public event EventHandler CanExecuteChanged
    		{
    			add { CommandManager.RequerySuggested += value; }
    			remove { CommandManager.RequerySuggested -= value; }
    		}
    	}
    }
    

    Result:

    x


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.